blob: 7a7b8754573a0a2083236abc6f08ee29f9db04f3 [file] [log] [blame]
openvcdiff311c7142008-08-26 19:29:25 +00001// 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()) {
openvcdiff@gmail.com732fff22010-08-04 18:00:00 +000028 VCD_DFATAL << "Internal error: position advanced by " << number_of_bytes
29 << " bytes, current unparsed size " << UnparsedSize()
30 << VCD_ENDL;
openvcdiff311c7142008-08-26 19:29:25 +000031 position_ = end_;
32 return;
33 }
34 position_ += number_of_bytes;
35}
36
37void ParseableChunk::SetPosition(const char* position) {
38 if (position < start_) {
openvcdiff@gmail.com732fff22010-08-04 18:00:00 +000039 VCD_DFATAL << "Internal error: new data position " << position
40 << " is beyond start of data " << start_ << VCD_ENDL;
openvcdiff311c7142008-08-26 19:29:25 +000041 position_ = start_;
42 return;
43 }
44 if (position > end_) {
openvcdiff@gmail.com732fff22010-08-04 18:00:00 +000045 VCD_DFATAL << "Internal error: new data position " << position
46 << " is beyond end of data " << end_ << VCD_ENDL;
openvcdiff311c7142008-08-26 19:29:25 +000047 position_ = end_;
48 return;
49 }
50 position_ = position;
51}
52
53void ParseableChunk::FinishExcept(size_t number_of_bytes) {
54 if (number_of_bytes > UnparsedSize()) {
openvcdiff@gmail.com732fff22010-08-04 18:00:00 +000055 VCD_DFATAL << "Internal error: specified number of remaining bytes "
56 << number_of_bytes << " is greater than unparsed data size "
57 << UnparsedSize() << VCD_ENDL;
openvcdiff311c7142008-08-26 19:29:25 +000058 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:
openvcdiff@gmail.com732fff22010-08-04 18:00:00 +000096 VCD_ERROR << "Expected " << variable_description
97 << "; found invalid variable-length integer" << VCD_ENDL;
openvcdiff311c7142008-08-26 19:29:25 +000098 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:
openvcdiff@gmail.com732fff22010-08-04 18:00:00 +0000123 VCD_ERROR << "Expected " << variable_description
124 << "; found invalid variable-length integer" << VCD_ENDL;
openvcdiff311c7142008-08-26 19:29:25 +0000125 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.com732fff22010-08-04 18:00:00 +0000132 VCD_ERROR << "Value of " << variable_description << "(" << parsed_value
133 << ") is too large for unsigned 32-bit integer" << VCD_ENDL;
openvcdiff311c7142008-08-26 19:29:25 +0000134 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) {
openvcdiff@gmail.com732fff22010-08-04 18:00:00 +0000176 VCD_ERROR << "Source segment length (" << *source_segment_length
177 << ") is larger than " << from_name << " (" << from_size
178 << ")" << VCD_ENDL;
openvcdiff311c7142008-08-26 19:29:25 +0000179 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.com732fff22010-08-04 18:00:00 +0000187 VCD_ERROR << "Source segment position (" << *source_segment_position
188 << ") is past " << from_boundary_name
189 << " (" << from_size << ")" << VCD_ENDL;
openvcdiff311c7142008-08-26 19:29:25 +0000190 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.com732fff22010-08-04 18:00:00 +0000196 VCD_ERROR << "Source segment end position (" << source_segment_end
197 << ") is past " << from_boundary_name
198 << " (" << from_size << ")" << VCD_ENDL;
openvcdiff311c7142008-08-26 19:29:25 +0000199 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,
openvcdiffbaf44ea2009-04-09 19:20:49 +0000208 bool allow_vcd_target,
openvcdiff311c7142008-08-26 19:29:25 +0000209 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:
openvcdiffbaf44ea2009-04-09 19:20:49 +0000225 if (!allow_vcd_target) {
openvcdiff@gmail.com732fff22010-08-04 18:00:00 +0000226 VCD_ERROR << "Delta file contains VCD_TARGET flag, which is not "
227 "allowed by current decoder settings" << VCD_ENDL;
openvcdiffbaf44ea2009-04-09 19:20:49 +0000228 return_code_ = RESULT_ERROR;
229 return false;
230 }
openvcdiff311c7142008-08-26 19:29:25 +0000231 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.com732fff22010-08-04 18:00:00 +0000237 VCD_ERROR << "Win_Indicator must not have both VCD_SOURCE"
238 " and VCD_TARGET set" << VCD_ENDL;
openvcdiff311c7142008-08-26 19:29:25 +0000239 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_) {
openvcdiff@gmail.com732fff22010-08-04 18:00:00 +0000248 VCD_DFATAL << "Internal error: VCDiffHeaderParser::ParseWindowLengths "
249 "was called twice for the same delta window" << VCD_ENDL;
openvcdiff311c7142008-08-26 19:29:25 +0000250 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 }
openvcdiff311c7142008-08-26 19:29:25 +0000260 return true;
261}
262
263const char* VCDiffHeaderParser::EndOfDeltaWindow() const {
264 if (!delta_encoding_start_) {
openvcdiff@gmail.com732fff22010-08-04 18:00:00 +0000265 VCD_DFATAL << "Internal error: VCDiffHeaderParser::GetDeltaWindowEnd "
266 "was called before ParseWindowLengths" << VCD_ENDL;
openvcdiff311c7142008-08-26 19:29:25 +0000267 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)) {
openvcdiff@gmail.com732fff22010-08-04 18:00:00 +0000278 VCD_ERROR << "Secondary compression of delta file sections "
279 "is not supported" << VCD_ENDL;
openvcdiff311c7142008-08-26 19:29:25 +0000280 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_) {
openvcdiff@gmail.com732fff22010-08-04 18:00:00 +0000302 VCD_DFATAL << "Internal error: VCDiffHeaderParser::ParseSectionLengths "
303 "was called before ParseWindowLengths" << VCD_ENDL;
openvcdiff311c7142008-08-26 19:29:25 +0000304 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.com732fff22010-08-04 18:00:00 +0000314 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;
openvcdiff311c7142008-08-26 19:29:25 +0000317 return_code_ = RESULT_ERROR;
318 return false;
319 }
320 return true;
321}
322
323} // namespace open_vcdiff