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
22namespace open_vcdiff {
23
24// *** Methods for ParseableChunk
25
26void ParseableChunk::Advance(size_t number_of_bytes) {
27  if (number_of_bytes > UnparsedSize()) {
28    VCD_DFATAL << "Internal error: position advanced by " << number_of_bytes
29               << " bytes, current unparsed size " << UnparsedSize()
30               << VCD_ENDL;
31    position_ = end_;
32    return;
33  }
34  position_ += number_of_bytes;
35}
36
37void ParseableChunk::SetPosition(const char* position) {
38  if (position < start_) {
39    VCD_DFATAL << "Internal error: new data position " << position
40               << " is beyond start of data " << start_ << VCD_ENDL;
41    position_ = start_;
42    return;
43  }
44  if (position > end_) {
45    VCD_DFATAL << "Internal error: new data position " << position
46               << " is beyond end of data " << end_ << VCD_ENDL;
47    position_ = end_;
48    return;
49  }
50  position_ = position;
51}
52
53void ParseableChunk::FinishExcept(size_t number_of_bytes) {
54  if (number_of_bytes > UnparsedSize()) {
55    VCD_DFATAL << "Internal error: specified number of remaining bytes "
56               << number_of_bytes << " is greater than unparsed data size "
57               << UnparsedSize() << VCD_ENDL;
58    Finish();
59    return;
60  }
61  position_ = end_ - number_of_bytes;
62}
63
64// *** Methods for VCDiffHeaderParser
65
66VCDiffHeaderParser::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
73bool 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
86bool 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:
96      VCD_ERROR << "Expected " << variable_description
97                << "; found invalid variable-length integer" << VCD_ENDL;
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.
113bool 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:
123      VCD_ERROR << "Expected " << variable_description
124                << "; found invalid variable-length integer" << VCD_ENDL;
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) {
132        VCD_ERROR << "Value of " << variable_description << "(" << parsed_value
133                  << ") is too large for unsigned 32-bit integer" << VCD_ENDL;
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.
144bool 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
154bool 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
164bool 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) {
176    VCD_ERROR << "Source segment length (" << *source_segment_length
177              << ") is larger than " << from_name << " (" << from_size
178              << ")" << VCD_ENDL;
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)) {
187    VCD_ERROR << "Source segment position (" << *source_segment_position
188              << ") is past " << from_boundary_name
189              << " (" << from_size << ")" << VCD_ENDL;
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) {
196    VCD_ERROR << "Source segment end position (" << source_segment_end
197              << ") is past " << from_boundary_name
198              << " (" << from_size << ")" << VCD_ENDL;
199    return_code_ = RESULT_ERROR;
200    return false;
201  }
202  return true;
203}
204
205bool VCDiffHeaderParser::ParseWinIndicatorAndSourceSegment(
206    size_t dictionary_size,
207    size_t decoded_target_size,
208    bool allow_vcd_target,
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:
225      if (!allow_vcd_target) {
226        VCD_ERROR << "Delta file contains VCD_TARGET flag, which is not "
227                     "allowed by current decoder settings" << VCD_ENDL;
228        return_code_ = RESULT_ERROR;
229        return false;
230      }
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:
237      VCD_ERROR << "Win_Indicator must not have both VCD_SOURCE"
238                   " and VCD_TARGET set" << VCD_ENDL;
239      return_code_ = RESULT_ERROR;
240      return false;
241    default:
242      return true;
243  }
244}
245
246bool VCDiffHeaderParser::ParseWindowLengths(size_t* target_window_length) {
247  if (delta_encoding_start_) {
248    VCD_DFATAL << "Internal error: VCDiffHeaderParser::ParseWindowLengths "
249                  "was called twice for the same delta window" << VCD_ENDL;
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  }
260  return true;
261}
262
263const char* VCDiffHeaderParser::EndOfDeltaWindow() const {
264  if (!delta_encoding_start_) {
265    VCD_DFATAL << "Internal error: VCDiffHeaderParser::GetDeltaWindowEnd "
266                  "was called before ParseWindowLengths" << VCD_ENDL;
267    return NULL;
268  }
269  return delta_encoding_start_ + delta_encoding_length_;
270}
271
272bool 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)) {
278    VCD_ERROR << "Secondary compression of delta file sections "
279                 "is not supported" << VCD_ENDL;
280    return_code_ = RESULT_ERROR;
281    return false;
282  }
283  return true;
284}
285
286bool 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_) {
302    VCD_DFATAL << "Internal error: VCDiffHeaderParser::ParseSectionLengths "
303                  "was called before ParseWindowLengths" << VCD_ENDL;
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)) {
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;
317    return_code_ = RESULT_ERROR;
318    return false;
319  }
320  return true;
321}
322
323}  // namespace open_vcdiff
324