openvcdiff | 311c714 | 2008-08-26 19:29:25 +0000 | [diff] [blame] | 1 | // Copyright 2008 Google Inc. |
| 2 | // Author: Lincoln Smith |
| 3 | // |
| 4 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | // you may not use this file except in compliance with the License. |
| 6 | // You may obtain a copy of the License at |
| 7 | // |
| 8 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | // |
| 10 | // Unless required by applicable law or agreed to in writing, software |
| 11 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | // See the License for the specific language governing permissions and |
| 14 | // limitations under the License. |
| 15 | |
| 16 | #include <config.h> |
| 17 | #include "headerparser.h" |
| 18 | #include "logging.h" |
| 19 | #include "varint_bigendian.h" |
| 20 | #include "vcdiff_defs.h" |
| 21 | |
| 22 | namespace open_vcdiff { |
| 23 | |
| 24 | // *** Methods for ParseableChunk |
| 25 | |
| 26 | void ParseableChunk::Advance(size_t number_of_bytes) { |
| 27 | if (number_of_bytes > UnparsedSize()) { |
openvcdiff@gmail.com | 732fff2 | 2010-08-04 18:00:00 +0000 | [diff] [blame] | 28 | VCD_DFATAL << "Internal error: position advanced by " << number_of_bytes |
| 29 | << " bytes, current unparsed size " << UnparsedSize() |
| 30 | << VCD_ENDL; |
openvcdiff | 311c714 | 2008-08-26 19:29:25 +0000 | [diff] [blame] | 31 | position_ = end_; |
| 32 | return; |
| 33 | } |
| 34 | position_ += number_of_bytes; |
| 35 | } |
| 36 | |
| 37 | void ParseableChunk::SetPosition(const char* position) { |
| 38 | if (position < start_) { |
openvcdiff@gmail.com | 732fff2 | 2010-08-04 18:00:00 +0000 | [diff] [blame] | 39 | VCD_DFATAL << "Internal error: new data position " << position |
| 40 | << " is beyond start of data " << start_ << VCD_ENDL; |
openvcdiff | 311c714 | 2008-08-26 19:29:25 +0000 | [diff] [blame] | 41 | position_ = start_; |
| 42 | return; |
| 43 | } |
| 44 | if (position > end_) { |
openvcdiff@gmail.com | 732fff2 | 2010-08-04 18:00:00 +0000 | [diff] [blame] | 45 | VCD_DFATAL << "Internal error: new data position " << position |
| 46 | << " is beyond end of data " << end_ << VCD_ENDL; |
openvcdiff | 311c714 | 2008-08-26 19:29:25 +0000 | [diff] [blame] | 47 | position_ = end_; |
| 48 | return; |
| 49 | } |
| 50 | position_ = position; |
| 51 | } |
| 52 | |
| 53 | void ParseableChunk::FinishExcept(size_t number_of_bytes) { |
| 54 | if (number_of_bytes > UnparsedSize()) { |
openvcdiff@gmail.com | 732fff2 | 2010-08-04 18:00:00 +0000 | [diff] [blame] | 55 | VCD_DFATAL << "Internal error: specified number of remaining bytes " |
| 56 | << number_of_bytes << " is greater than unparsed data size " |
| 57 | << UnparsedSize() << VCD_ENDL; |
openvcdiff | 311c714 | 2008-08-26 19:29:25 +0000 | [diff] [blame] | 58 | Finish(); |
| 59 | return; |
| 60 | } |
| 61 | position_ = end_ - number_of_bytes; |
| 62 | } |
| 63 | |
| 64 | // *** Methods for VCDiffHeaderParser |
| 65 | |
| 66 | VCDiffHeaderParser::VCDiffHeaderParser(const char* header_start, |
| 67 | const char* data_end) |
| 68 | : parseable_chunk_(header_start, data_end - header_start), |
| 69 | return_code_(RESULT_SUCCESS), |
| 70 | delta_encoding_length_(0), |
| 71 | delta_encoding_start_(NULL) { } |
| 72 | |
| 73 | bool VCDiffHeaderParser::ParseByte(unsigned char* value) { |
| 74 | if (RESULT_SUCCESS != return_code_) { |
| 75 | return false; |
| 76 | } |
| 77 | if (parseable_chunk_.Empty()) { |
| 78 | return_code_ = RESULT_END_OF_DATA; |
| 79 | return false; |
| 80 | } |
| 81 | *value = static_cast<unsigned char>(*parseable_chunk_.UnparsedData()); |
| 82 | parseable_chunk_.Advance(1); |
| 83 | return true; |
| 84 | } |
| 85 | |
| 86 | bool VCDiffHeaderParser::ParseInt32(const char* variable_description, |
| 87 | int32_t* value) { |
| 88 | if (RESULT_SUCCESS != return_code_) { |
| 89 | return false; |
| 90 | } |
| 91 | int32_t parsed_value = |
| 92 | VarintBE<int32_t>::Parse(parseable_chunk_.End(), |
| 93 | parseable_chunk_.UnparsedDataAddr()); |
| 94 | switch (parsed_value) { |
| 95 | case RESULT_ERROR: |
openvcdiff@gmail.com | 732fff2 | 2010-08-04 18:00:00 +0000 | [diff] [blame] | 96 | VCD_ERROR << "Expected " << variable_description |
| 97 | << "; found invalid variable-length integer" << VCD_ENDL; |
openvcdiff | 311c714 | 2008-08-26 19:29:25 +0000 | [diff] [blame] | 98 | return_code_ = RESULT_ERROR; |
| 99 | return false; |
| 100 | case RESULT_END_OF_DATA: |
| 101 | return_code_ = RESULT_END_OF_DATA; |
| 102 | return false; |
| 103 | default: |
| 104 | *value = parsed_value; |
| 105 | return true; |
| 106 | } |
| 107 | } |
| 108 | |
| 109 | // When an unsigned 32-bit integer is expected, parse a signed 64-bit value |
| 110 | // instead, then check the value limit. The uint32_t type can't be parsed |
| 111 | // directly because two negative values are given special meanings (RESULT_ERROR |
| 112 | // and RESULT_END_OF_DATA) and could not be expressed in an unsigned format. |
| 113 | bool VCDiffHeaderParser::ParseUInt32(const char* variable_description, |
| 114 | uint32_t* value) { |
| 115 | if (RESULT_SUCCESS != return_code_) { |
| 116 | return false; |
| 117 | } |
| 118 | int64_t parsed_value = |
| 119 | VarintBE<int64_t>::Parse(parseable_chunk_.End(), |
| 120 | parseable_chunk_.UnparsedDataAddr()); |
| 121 | switch (parsed_value) { |
| 122 | case RESULT_ERROR: |
openvcdiff@gmail.com | 732fff2 | 2010-08-04 18:00:00 +0000 | [diff] [blame] | 123 | VCD_ERROR << "Expected " << variable_description |
| 124 | << "; found invalid variable-length integer" << VCD_ENDL; |
openvcdiff | 311c714 | 2008-08-26 19:29:25 +0000 | [diff] [blame] | 125 | return_code_ = RESULT_ERROR; |
| 126 | return false; |
| 127 | case RESULT_END_OF_DATA: |
| 128 | return_code_ = RESULT_END_OF_DATA; |
| 129 | return false; |
| 130 | default: |
| 131 | if (parsed_value > 0xFFFFFFFF) { |
openvcdiff@gmail.com | 732fff2 | 2010-08-04 18:00:00 +0000 | [diff] [blame] | 132 | VCD_ERROR << "Value of " << variable_description << "(" << parsed_value |
| 133 | << ") is too large for unsigned 32-bit integer" << VCD_ENDL; |
openvcdiff | 311c714 | 2008-08-26 19:29:25 +0000 | [diff] [blame] | 134 | return_code_ = RESULT_ERROR; |
| 135 | return false; |
| 136 | } |
| 137 | *value = static_cast<uint32_t>(parsed_value); |
| 138 | return true; |
| 139 | } |
| 140 | } |
| 141 | |
| 142 | // A VCDChecksum represents an unsigned 32-bit value returned by adler32(), |
| 143 | // but isn't a uint32_t. |
| 144 | bool VCDiffHeaderParser::ParseChecksum(const char* variable_description, |
| 145 | VCDChecksum* value) { |
| 146 | uint32_t parsed_value = 0; |
| 147 | if (!ParseUInt32(variable_description, &parsed_value)) { |
| 148 | return false; |
| 149 | } |
| 150 | *value = static_cast<VCDChecksum>(parsed_value); |
| 151 | return true; |
| 152 | } |
| 153 | |
| 154 | bool VCDiffHeaderParser::ParseSize(const char* variable_description, |
| 155 | size_t* value) { |
| 156 | int32_t parsed_value = 0; |
| 157 | if (!ParseInt32(variable_description, &parsed_value)) { |
| 158 | return false; |
| 159 | } |
| 160 | *value = static_cast<size_t>(parsed_value); |
| 161 | return true; |
| 162 | } |
| 163 | |
| 164 | bool VCDiffHeaderParser::ParseSourceSegmentLengthAndPosition( |
| 165 | size_t from_size, |
| 166 | const char* from_boundary_name, |
| 167 | const char* from_name, |
| 168 | size_t* source_segment_length, |
| 169 | size_t* source_segment_position) { |
| 170 | // Verify the length and position values |
| 171 | if (!ParseSize("source segment length", source_segment_length)) { |
| 172 | return false; |
| 173 | } |
| 174 | // Guard against overflow by checking source length first |
| 175 | if (*source_segment_length > from_size) { |
openvcdiff@gmail.com | 732fff2 | 2010-08-04 18:00:00 +0000 | [diff] [blame] | 176 | VCD_ERROR << "Source segment length (" << *source_segment_length |
| 177 | << ") is larger than " << from_name << " (" << from_size |
| 178 | << ")" << VCD_ENDL; |
openvcdiff | 311c714 | 2008-08-26 19:29:25 +0000 | [diff] [blame] | 179 | return_code_ = RESULT_ERROR; |
| 180 | return false; |
| 181 | } |
| 182 | if (!ParseSize("source segment position", source_segment_position)) { |
| 183 | return false; |
| 184 | } |
| 185 | if ((*source_segment_position >= from_size) && |
| 186 | (*source_segment_length > 0)) { |
openvcdiff@gmail.com | 732fff2 | 2010-08-04 18:00:00 +0000 | [diff] [blame] | 187 | VCD_ERROR << "Source segment position (" << *source_segment_position |
| 188 | << ") is past " << from_boundary_name |
| 189 | << " (" << from_size << ")" << VCD_ENDL; |
openvcdiff | 311c714 | 2008-08-26 19:29:25 +0000 | [diff] [blame] | 190 | return_code_ = RESULT_ERROR; |
| 191 | return false; |
| 192 | } |
| 193 | const size_t source_segment_end = *source_segment_position + |
| 194 | *source_segment_length; |
| 195 | if (source_segment_end > from_size) { |
openvcdiff@gmail.com | 732fff2 | 2010-08-04 18:00:00 +0000 | [diff] [blame] | 196 | VCD_ERROR << "Source segment end position (" << source_segment_end |
| 197 | << ") is past " << from_boundary_name |
| 198 | << " (" << from_size << ")" << VCD_ENDL; |
openvcdiff | 311c714 | 2008-08-26 19:29:25 +0000 | [diff] [blame] | 199 | return_code_ = RESULT_ERROR; |
| 200 | return false; |
| 201 | } |
| 202 | return true; |
| 203 | } |
| 204 | |
| 205 | bool VCDiffHeaderParser::ParseWinIndicatorAndSourceSegment( |
| 206 | size_t dictionary_size, |
| 207 | size_t decoded_target_size, |
openvcdiff | baf44ea | 2009-04-09 19:20:49 +0000 | [diff] [blame] | 208 | bool allow_vcd_target, |
openvcdiff | 311c714 | 2008-08-26 19:29:25 +0000 | [diff] [blame] | 209 | unsigned char* win_indicator, |
| 210 | size_t* source_segment_length, |
| 211 | size_t* source_segment_position) { |
| 212 | if (!ParseByte(win_indicator)) { |
| 213 | return false; |
| 214 | } |
| 215 | unsigned char source_target_flags = |
| 216 | *win_indicator & (VCD_SOURCE | VCD_TARGET); |
| 217 | switch (source_target_flags) { |
| 218 | case VCD_SOURCE: |
| 219 | return ParseSourceSegmentLengthAndPosition(dictionary_size, |
| 220 | "end of dictionary", |
| 221 | "dictionary", |
| 222 | source_segment_length, |
| 223 | source_segment_position); |
| 224 | case VCD_TARGET: |
openvcdiff | baf44ea | 2009-04-09 19:20:49 +0000 | [diff] [blame] | 225 | if (!allow_vcd_target) { |
openvcdiff@gmail.com | 732fff2 | 2010-08-04 18:00:00 +0000 | [diff] [blame] | 226 | VCD_ERROR << "Delta file contains VCD_TARGET flag, which is not " |
| 227 | "allowed by current decoder settings" << VCD_ENDL; |
openvcdiff | baf44ea | 2009-04-09 19:20:49 +0000 | [diff] [blame] | 228 | return_code_ = RESULT_ERROR; |
| 229 | return false; |
| 230 | } |
openvcdiff | 311c714 | 2008-08-26 19:29:25 +0000 | [diff] [blame] | 231 | return ParseSourceSegmentLengthAndPosition(decoded_target_size, |
| 232 | "current target position", |
| 233 | "target file", |
| 234 | source_segment_length, |
| 235 | source_segment_position); |
| 236 | case VCD_SOURCE | VCD_TARGET: |
openvcdiff@gmail.com | 732fff2 | 2010-08-04 18:00:00 +0000 | [diff] [blame] | 237 | VCD_ERROR << "Win_Indicator must not have both VCD_SOURCE" |
| 238 | " and VCD_TARGET set" << VCD_ENDL; |
openvcdiff | 311c714 | 2008-08-26 19:29:25 +0000 | [diff] [blame] | 239 | return_code_ = RESULT_ERROR; |
| 240 | return false; |
| 241 | default: |
| 242 | return true; |
| 243 | } |
| 244 | } |
| 245 | |
| 246 | bool VCDiffHeaderParser::ParseWindowLengths(size_t* target_window_length) { |
| 247 | if (delta_encoding_start_) { |
openvcdiff@gmail.com | 732fff2 | 2010-08-04 18:00:00 +0000 | [diff] [blame] | 248 | VCD_DFATAL << "Internal error: VCDiffHeaderParser::ParseWindowLengths " |
| 249 | "was called twice for the same delta window" << VCD_ENDL; |
openvcdiff | 311c714 | 2008-08-26 19:29:25 +0000 | [diff] [blame] | 250 | return_code_ = RESULT_ERROR; |
| 251 | return false; |
| 252 | } |
| 253 | if (!ParseSize("length of the delta encoding", &delta_encoding_length_)) { |
| 254 | return false; |
| 255 | } |
| 256 | delta_encoding_start_ = UnparsedData(); |
| 257 | if (!ParseSize("size of the target window", target_window_length)) { |
| 258 | return false; |
| 259 | } |
openvcdiff | 311c714 | 2008-08-26 19:29:25 +0000 | [diff] [blame] | 260 | return true; |
| 261 | } |
| 262 | |
| 263 | const char* VCDiffHeaderParser::EndOfDeltaWindow() const { |
| 264 | if (!delta_encoding_start_) { |
openvcdiff@gmail.com | 732fff2 | 2010-08-04 18:00:00 +0000 | [diff] [blame] | 265 | VCD_DFATAL << "Internal error: VCDiffHeaderParser::GetDeltaWindowEnd " |
| 266 | "was called before ParseWindowLengths" << VCD_ENDL; |
openvcdiff | 311c714 | 2008-08-26 19:29:25 +0000 | [diff] [blame] | 267 | return NULL; |
| 268 | } |
| 269 | return delta_encoding_start_ + delta_encoding_length_; |
| 270 | } |
| 271 | |
| 272 | bool VCDiffHeaderParser::ParseDeltaIndicator() { |
| 273 | unsigned char delta_indicator; |
| 274 | if (!ParseByte(&delta_indicator)) { |
| 275 | return false; |
| 276 | } |
| 277 | if (delta_indicator & (VCD_DATACOMP | VCD_INSTCOMP | VCD_ADDRCOMP)) { |
openvcdiff@gmail.com | 732fff2 | 2010-08-04 18:00:00 +0000 | [diff] [blame] | 278 | VCD_ERROR << "Secondary compression of delta file sections " |
| 279 | "is not supported" << VCD_ENDL; |
openvcdiff | 311c714 | 2008-08-26 19:29:25 +0000 | [diff] [blame] | 280 | return_code_ = RESULT_ERROR; |
| 281 | return false; |
| 282 | } |
| 283 | return true; |
| 284 | } |
| 285 | |
| 286 | bool VCDiffHeaderParser::ParseSectionLengths( |
| 287 | bool has_checksum, |
| 288 | size_t* add_and_run_data_length, |
| 289 | size_t* instructions_and_sizes_length, |
| 290 | size_t* addresses_length, |
| 291 | VCDChecksum* checksum) { |
| 292 | ParseSize("length of data for ADDs and RUNs", add_and_run_data_length); |
| 293 | ParseSize("length of instructions section", instructions_and_sizes_length); |
| 294 | ParseSize("length of addresses for COPYs", addresses_length); |
| 295 | if (has_checksum) { |
| 296 | ParseChecksum("Adler32 checksum value", checksum); |
| 297 | } |
| 298 | if (RESULT_SUCCESS != return_code_) { |
| 299 | return false; |
| 300 | } |
| 301 | if (!delta_encoding_start_) { |
openvcdiff@gmail.com | 732fff2 | 2010-08-04 18:00:00 +0000 | [diff] [blame] | 302 | VCD_DFATAL << "Internal error: VCDiffHeaderParser::ParseSectionLengths " |
| 303 | "was called before ParseWindowLengths" << VCD_ENDL; |
openvcdiff | 311c714 | 2008-08-26 19:29:25 +0000 | [diff] [blame] | 304 | return_code_ = RESULT_ERROR; |
| 305 | return false; |
| 306 | } |
| 307 | const size_t delta_encoding_header_length = |
| 308 | UnparsedData() - delta_encoding_start_; |
| 309 | if (delta_encoding_length_ != |
| 310 | (delta_encoding_header_length + |
| 311 | *add_and_run_data_length + |
| 312 | *instructions_and_sizes_length + |
| 313 | *addresses_length)) { |
openvcdiff@gmail.com | 732fff2 | 2010-08-04 18:00:00 +0000 | [diff] [blame] | 314 | VCD_ERROR << "The length of the delta encoding does not match " |
| 315 | "the size of the header plus the sizes of the data sections" |
| 316 | << VCD_ENDL; |
openvcdiff | 311c714 | 2008-08-26 19:29:25 +0000 | [diff] [blame] | 317 | return_code_ = RESULT_ERROR; |
| 318 | return false; |
| 319 | } |
| 320 | return true; |
| 321 | } |
| 322 | |
| 323 | } // namespace open_vcdiff |