blob: 500ee7788819296a8ec9cfdb8a1b35461747e24f [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// VCDiffCodeTableReader is a class to interpret a stream of opcodes
17// as VCDIFF instruction types, based on a VCDiffCodeTableData structure.
18
19#include <config.h>
20#include "decodetable.h"
21#include "codetable.h"
22#include "logging.h"
23#include "varint_bigendian.h"
24#include "vcdiff_defs.h"
25
26namespace open_vcdiff {
27
28VCDiffCodeTableReader::VCDiffCodeTableReader()
29 : code_table_data_(&VCDiffCodeTableData::kDefaultCodeTableData),
30 non_default_code_table_data_(NULL),
31 instructions_and_sizes_(NULL),
32 instructions_and_sizes_end_(NULL),
33 last_instruction_start_(NULL),
34 pending_second_instruction_(kNoOpcode),
35 last_pending_second_instruction_(kNoOpcode) {
36}
37
38bool VCDiffCodeTableReader::UseCodeTable(
39 const VCDiffCodeTableData& code_table_data, unsigned char max_mode) {
40 if (!code_table_data.Validate(max_mode)) return false;
41 if (!non_default_code_table_data_.get()) {
42 non_default_code_table_data_.reset(new VCDiffCodeTableData);
43 }
44 *non_default_code_table_data_ = code_table_data;
45 code_table_data_ = non_default_code_table_data_.get();
46 return true;
47}
48
49VCDiffInstructionType VCDiffCodeTableReader::GetNextInstruction(
50 int32_t* size,
51 unsigned char* mode) {
52 if (!instructions_and_sizes_) {
openvcdiff@gmail.com732fff22010-08-04 18:00:00 +000053 VCD_ERROR << "Internal error: GetNextInstruction() called before Init()"
54 << VCD_ENDL;
openvcdiff311c7142008-08-26 19:29:25 +000055 return VCD_INSTRUCTION_ERROR;
56 }
57 last_instruction_start_ = *instructions_and_sizes_;
58 last_pending_second_instruction_ = pending_second_instruction_;
59 unsigned char opcode = 0;
60 unsigned char instruction_type = VCD_NOOP;
61 int32_t instruction_size = 0;
62 unsigned char instruction_mode = 0;
63 do {
64 if (pending_second_instruction_ != kNoOpcode) {
65 // There is a second instruction left over
66 // from the most recently processed opcode.
67 opcode = static_cast<unsigned char>(pending_second_instruction_);
68 pending_second_instruction_ = kNoOpcode;
69 instruction_type = code_table_data_->inst2[opcode];
70 instruction_size = code_table_data_->size2[opcode];
71 instruction_mode = code_table_data_->mode2[opcode];
72 break;
73 }
74 if (*instructions_and_sizes_ >= instructions_and_sizes_end_) {
75 // Ran off end of instruction stream
76 return VCD_INSTRUCTION_END_OF_DATA;
77 }
78 opcode = **instructions_and_sizes_;
79 if (code_table_data_->inst2[opcode] != VCD_NOOP) {
80 // This opcode contains two instructions; process the first one now, and
81 // save a pointer to the second instruction, which should be returned
82 // by the next call to GetNextInstruction
83 pending_second_instruction_ = **instructions_and_sizes_;
84 }
85 ++(*instructions_and_sizes_);
86 instruction_type = code_table_data_->inst1[opcode];
87 instruction_size = code_table_data_->size1[opcode];
88 instruction_mode = code_table_data_->mode1[opcode];
89 // This do-while loop is necessary in case inst1 == VCD_NOOP for an opcode
90 // that was actually used in the encoding. That case is unusual, but it
91 // is not prohibited by the standard.
92 } while (instruction_type == VCD_NOOP);
93 if (instruction_size == 0) {
94 // Parse the size as a Varint in the instruction stream.
95 switch (*size = VarintBE<int32_t>::Parse(instructions_and_sizes_end_,
96 instructions_and_sizes_)) {
97 case RESULT_ERROR:
openvcdiff@gmail.com732fff22010-08-04 18:00:00 +000098 VCD_ERROR << "Instruction size is not a valid variable-length integer"
99 << VCD_ENDL;
openvcdiff311c7142008-08-26 19:29:25 +0000100 return VCD_INSTRUCTION_ERROR;
101 case RESULT_END_OF_DATA:
102 UnGetInstruction(); // Rewind to instruction start
103 return VCD_INSTRUCTION_END_OF_DATA;
104 default:
105 break; // Successfully parsed Varint
106 }
107 } else {
108 *size = instruction_size;
109 }
110 *mode = instruction_mode;
111 return static_cast<VCDiffInstructionType>(instruction_type);
112}
113
114}; // namespace open_vcdiff