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// 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_) {
53    VCD_ERROR << "Internal error: GetNextInstruction() called before Init()"
54              << VCD_ENDL;
55    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:
98        VCD_ERROR << "Instruction size is not a valid variable-length integer"
99                  << VCD_ENDL;
100        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
115