Home | History | Annotate | Download | only in src
      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     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