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 26 namespace open_vcdiff { 27 28 VCDiffCodeTableReader::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 38 bool 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 49 VCDiffInstructionType 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