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 LOG(ERROR) << "Internal error: GetNextInstruction() called before Init()" 54 << LOG_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 LOG(ERROR) << "Instruction size is not a valid variable-length integer" 99 << LOG_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