// Copyright 2008 Google Inc.
// Author: Lincoln Smith
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Implements a Decoder for the format described in
// RFC 3284 - The VCDIFF Generic Differencing and Compression Data Format.
// The RFC text can be found at http://www.faqs.org/rfcs/rfc3284.html
//
// The RFC describes the possibility of using a secondary compressor
// to further reduce the size of each section of the VCDIFF output.
// That feature is not supported in this implementation of the encoder
// and decoder.
// No secondary compressor types have been publicly registered with
// the IANA at http://www.iana.org/assignments/vcdiff-comp-ids
// in the more than five years since the registry was created, so there
// is no standard set of compressor IDs which would be generated by other
// encoders or accepted by other decoders.

#include <config.h>
#include "google/vcdecoder.h"
#include <stddef.h>  // size_t, ptrdiff_t
#include <stdint.h>  // int32_t
#include <string.h>  // memcpy, memset
#include <memory>  // auto_ptr
#include <string>
#include "addrcache.h"
#include "checksum.h"
#include "codetable.h"
#include "decodetable.h"
#include "headerparser.h"
#include "logging.h"
#include "google/output_string.h"
#include "varint_bigendian.h"
#include "vcdiff_defs.h"

namespace open_vcdiff {

// This class is used to parse delta file windows as described
// in RFC sections 4.2 and 4.3.  Its methods are not thread-safe.
//
// Here is the window format copied from the RFC:
//
// Window1
//     Win_Indicator                            - byte
//     [Source segment size]                    - integer
//     [Source segment position]                - integer
//     The delta encoding of the target window
//         Length of the delta encoding         - integer
//         The delta encoding
//             Size of the target window        - integer
//             Delta_Indicator                  - byte
//             Length of data for ADDs and RUNs - integer
//             Length of instructions and sizes - integer
//             Length of addresses for COPYs    - integer
//             Data section for ADDs and RUNs   - array of bytes
//             Instructions and sizes section   - array of bytes
//             Addresses section for COPYs      - array of bytes
// Window2
// ...
//
// Sample usage:
//
// VCDiffDeltaFileWindow delta_window_;
// delta_window_.Init(parent);
// ParseableChunk parseable_chunk(input_buffer,
//                                input_size,
//                                leftover_unencoded_bytes);
// while (!parseable_chunk.Empty()) {
//   switch (delta_window_.DecodeWindow(&parseable_chunk)) {
//     case RESULT_END_OF_DATA:
//       <Read more input and retry DecodeWindow later.>
//     case RESULT_ERROR:
//       <Handle error case.  An error log message has already been generated.>
//   }
// }
//
// DecodeWindow consumes only a single window, and needs to be placed within
// a loop if multiple windows are to be processed.
//
class VCDiffDeltaFileWindow {
 public:
  VCDiffDeltaFileWindow();
  ~VCDiffDeltaFileWindow();

  // Init() should be called immediately after constructing the
  // VCDiffDeltaFileWindow().  It must be called before DecodeWindow() can be
  // invoked, or an error will occur.
  void Init(VCDiffStreamingDecoderImpl* parent);

  // Resets the pointers to the data sections in the current window.
  void Reset();

  bool UseCodeTable(const VCDiffCodeTableData& code_table_data,
                    unsigned char max_mode) {
    return reader_.UseCodeTable(code_table_data, max_mode);
  }

  // Decodes a single delta window using the input data from *parseable_chunk.
  // Appends the decoded target window to parent_->decoded_target().  Returns
  // RESULT_SUCCESS if an entire window was decoded, or RESULT_END_OF_DATA if
  // the end of input was reached before the entire window could be decoded and
  // more input is expected (only possible if IsInterleaved() is true), or
  // RESULT_ERROR if an error occurred during decoding.  In the RESULT_ERROR
  // case, the value of parseable_chunk->pointer_ is undefined; otherwise,
  // parseable_chunk->Advance() is called to point to the input data position
  // just after the data that has been decoded.
  //
  VCDiffResult DecodeWindow(ParseableChunk* parseable_chunk);

  bool FoundWindowHeader() const {
    return found_header_;
  }

  bool MoreDataExpected() const {
    // When parsing an interleaved-format delta file,
    // every time DecodeBody() exits, interleaved_bytes_expected_
    // will be decremented by the number of bytes parsed.  If it
    // reaches zero, then there is no more data expected because
    // the size of the interleaved section (given in the window
    // header) has been reached.
    return IsInterleaved() && (interleaved_bytes_expected_ > 0);
  }

  size_t target_window_start_pos() const { return target_window_start_pos_; }

  void set_target_window_start_pos(size_t new_start_pos) {
    target_window_start_pos_ = new_start_pos;
  }

  // Returns the number of bytes remaining to be decoded in the target window.
  // If not in the process of decoding a window, returns 0.
  size_t TargetBytesRemaining();

 private:
  // Reads the header of the window section as described in RFC sections 4.2 and
  // 4.3, up to and including the value "Length of addresses for COPYs".  If the
  // entire header is found, this function sets up the DeltaWindowSections
  // instructions_and_sizes_, data_for_add_and_run_, and addresses_for_copy_ so
  // that the decoder can begin decoding the opcodes in these sections.  Returns
  // RESULT_ERROR if an error occurred, or RESULT_END_OF_DATA if the end of
  // available data was reached before the entire header could be read.  (The
  // latter may be an error condition if there is no more data available.)
  // Otherwise, returns RESULT_SUCCESS and advances parseable_chunk past the
  // parsed header.
  //
  VCDiffResult ReadHeader(ParseableChunk* parseable_chunk);

  // After the window header has been parsed as far as the Delta_Indicator,
  // this function is called to parse the following delta window header fields:
  //
  //     Length of data for ADDs and RUNs - integer (VarintBE format)
  //     Length of instructions and sizes - integer (VarintBE format)
  //     Length of addresses for COPYs    - integer (VarintBE format)
  //
  // If has_checksum_ is true, it also looks for the following element:
  //
  //     Adler32 checksum            - unsigned 32-bit integer (VarintBE format)
  //
  // It sets up the DeltaWindowSections instructions_and_sizes_,
  // data_for_add_and_run_, and addresses_for_copy_.  If the interleaved format
  // is being used, all three sections will include the entire window body; if
  // the standard format is used, three non-overlapping window sections will be
  // defined.  Returns RESULT_ERROR if an error occurred, or RESULT_END_OF_DATA
  // if standard format is being used and there is not enough input data to read
  // the entire window body.  Otherwise, returns RESULT_SUCCESS.
  VCDiffResult SetUpWindowSections(VCDiffHeaderParser* header_parser);

  // Decodes the body of the window section as described in RFC sections 4.3,
  // including the sections "Data section for ADDs and RUNs", "Instructions
  // and sizes section", and "Addresses section for COPYs".  These sections
  // must already have been set up by ReadWindowHeader().  Returns a
  // non-negative value on success, or RESULT_END_OF_DATA if the end of input
  // was reached before the entire window could be decoded (only possible if
  // IsInterleaved() is true), or RESULT_ERROR if an error occurred during
  // decoding.  Appends as much of the decoded target window as possible to
  // parent->decoded_target().
  //
  int DecodeBody(ParseableChunk* parseable_chunk);

  // Returns the number of bytes already decoded into the target window.
  size_t TargetBytesDecoded();

  // Decodes a single ADD instruction, updating parent_->decoded_target_.
  VCDiffResult DecodeAdd(size_t size);

  // Decodes a single RUN instruction, updating parent_->decoded_target_.
  VCDiffResult DecodeRun(size_t size);

  // Decodes a single COPY instruction, updating parent_->decoded_target_.
  VCDiffResult DecodeCopy(size_t size, unsigned char mode);

  // When using the interleaved format, this function is called both on parsing
  // the header and on resuming after a RESULT_END_OF_DATA was returned from a
  // previous call to DecodeBody().  It sets up all three section pointers to
  // reference the same interleaved stream of instructions, sizes, addresses,
  // and data.  These pointers must be reset every time that work resumes on a
  // delta window,  because the input data string may have been changed or
  // resized since DecodeBody() last returned.
  void UpdateInterleavedSectionPointers(const char* data_pos,
                                        const char* data_end) {
    const ptrdiff_t available_data = data_end - data_pos;
    // Don't read past the end of currently-available data
    if (available_data > interleaved_bytes_expected_) {
      instructions_and_sizes_.Init(data_pos, interleaved_bytes_expected_);
    } else {
      instructions_and_sizes_.Init(data_pos, available_data);
    }
    data_for_add_and_run_.Init(&instructions_and_sizes_);
    addresses_for_copy_.Init(&instructions_and_sizes_);
  }

  // If true, the interleaved format described in AllowInterleaved() is used
  // for the current delta file.  Only valid after ReadWindowHeader() has been
  // called and returned a positive number (i.e., the whole header was parsed),
  // but before the window has finished decoding.
  //
  bool IsInterleaved() const {
    // If the sections are interleaved, both addresses_for_copy_ and
    // data_for_add_and_run_ should point at instructions_and_sizes_.
    return !addresses_for_copy_.IsOwned();
  }

  // Executes a single COPY or ADD instruction, appending data to
  // parent_->decoded_target().
  void CopyBytes(const char* data, size_t size);

  // Executes a single RUN instruction, appending data to
  // parent_->decoded_target().
  void RunByte(unsigned char byte, size_t size);

  // Advance *parseable_chunk to point to the current position in the
  // instructions/sizes section.  If interleaved format is used, then
  // decrement the number of expected bytes in the instructions/sizes section
  // by the number of instruction/size bytes parsed.
  void UpdateInstructionPointer(ParseableChunk* parseable_chunk);

  // The parent object which was passed to Init().
  VCDiffStreamingDecoderImpl* parent_;

  // This value will be true if VCDiffDeltaFileWindow::ReadDeltaWindowHeader()
  // has been called and succeeded in parsing the delta window header, but the
  // entire window has not yet been decoded.
  bool found_header_;

  // Contents and length of the current source window.  source_segment_ptr_
  // will be non-NULL if (a) the window section header for the current window
  // has been read, but the window has not yet finished decoding; or
  // (b) the window did not specify a source segment.
  const char* source_segment_ptr_;
  size_t source_segment_length_;

  // The delta encoding window sections as defined in RFC section 4.3.
  // The pointer for each section will be incremented as data is consumed and
  // decoded from that section.  If the interleaved format is used,
  // data_for_add_and_run_ and addresses_for_copy_ will both point to
  // instructions_and_sizes_; otherwise, they will be separate data sections.
  //
  DeltaWindowSection instructions_and_sizes_;
  DeltaWindowSection data_for_add_and_run_;
  DeltaWindowSection addresses_for_copy_;

  // The expected bytes left to decode in instructions_and_sizes_.  Only used
  // for the interleaved format.
  int interleaved_bytes_expected_;

  // The expected length of the target window once it has been decoded.
  size_t target_window_length_;

  // The index in decoded_target at which the first byte of the current
  // target window was/will be written.
  size_t target_window_start_pos_;

  // If has_checksum_ is true, then expected_checksum_ contains an Adler32
  // checksum of the target window data.  This is an extension included in the
  // VCDIFF 'S' (SDCH) format, but is not part of the RFC 3284 draft standard.
  bool has_checksum_;
  VCDChecksum expected_checksum_;

  VCDiffCodeTableReader reader_;

  // Making these private avoids implicit copy constructor & assignment operator
  VCDiffDeltaFileWindow(const VCDiffDeltaFileWindow&);  // NOLINT
  void operator=(const VCDiffDeltaFileWindow&);
};

// *** Inline methods for VCDiffDeltaFileWindow

inline VCDiffDeltaFileWindow::VCDiffDeltaFileWindow() : parent_(NULL) {
  Reset();
}

inline VCDiffDeltaFileWindow::~VCDiffDeltaFileWindow() { }

inline void VCDiffDeltaFileWindow::Init(VCDiffStreamingDecoderImpl* parent) {
  parent_ = parent;
}

class VCDiffStreamingDecoderImpl {
 public:
  typedef std::string string;

  // The default maximum target file size (and target window size) if
  // SetMaximumTargetFileSize() is not called.
  static const size_t kDefaultMaximumTargetFileSize = 67108864U;  // 64 MB

  // The largest value that can be passed to SetMaximumTargetWindowSize().
  // Using a larger value will result in an error.
  static const size_t kTargetSizeLimit = 2147483647U;  // INT32_MAX

  // A constant that is the default value for planned_target_file_size_,
  // indicating that the decoder does not have an expected length
  // for the target data.
  static const size_t kUnlimitedBytes = static_cast<size_t>(-3);

  VCDiffStreamingDecoderImpl();
  ~VCDiffStreamingDecoderImpl();

  // Resets all member variables to their initial states.
  void Reset();

  // These functions are identical to their counterparts
  // in VCDiffStreamingDecoder.
  //
  void StartDecoding(const char* dictionary_ptr, size_t dictionary_size);

  bool DecodeChunk(const char* data,
                   size_t len,
                   OutputStringInterface* output_string);

  bool FinishDecoding();

  // If true, the version of VCDIFF used in the current delta file allows
  // for the interleaved format, in which instructions, addresses and data
  // are all sent interleaved in the instructions section of each window
  // rather than being sent in separate sections.  This is not part of
  // the VCDIFF draft standard, so we've defined a special version code
  // 'S' which implies that this feature is available.  Even if interleaving
  // is supported, it is not mandatory; interleaved format will be implied
  // if the address and data sections are both zero-length.
  //
  bool AllowInterleaved() const { return vcdiff_version_code_ == 'S'; }

  // If true, the version of VCDIFF used in the current delta file allows
  // each delta window to contain an Adler32 checksum of the target window data.
  // If the bit 0x08 (VCD_CHECKSUM) is set in the Win_Indicator flags, then
  // this checksum will appear as a variable-length integer, just after the
  // "length of addresses for COPYs" value and before the window data sections.
  // It is possible for some windows in a delta file to use the checksum feature
  // and for others not to use it (and leave the flag bit set to 0.)
  // Just as with AllowInterleaved(), this extension is not part of the draft
  // standard and is only available when the version code 'S' is specified.
  //
  bool AllowChecksum() const { return vcdiff_version_code_ == 'S'; }

  bool SetMaximumTargetFileSize(size_t new_maximum_target_file_size) {
    maximum_target_file_size_ = new_maximum_target_file_size;
    return true;
  }

  bool SetMaximumTargetWindowSize(size_t new_maximum_target_window_size) {
    if (new_maximum_target_window_size > kTargetSizeLimit) {
      VCD_ERROR << "Specified maximum target window size "
                << new_maximum_target_window_size << " exceeds limit of "
                << kTargetSizeLimit << " bytes" << VCD_ENDL;
      return false;
    }
    maximum_target_window_size_ = new_maximum_target_window_size;
    return true;
  }

  // See description of planned_target_file_size_, below.
  bool HasPlannedTargetFileSize() const {
    return planned_target_file_size_ != kUnlimitedBytes;
  }

  void SetPlannedTargetFileSize(size_t planned_target_file_size) {
    planned_target_file_size_ = planned_target_file_size;
  }

  void AddToTotalTargetWindowSize(size_t window_size) {
    total_of_target_window_sizes_ += window_size;
  }

  // Checks to see whether the decoded target data has reached its planned size.
  bool ReachedPlannedTargetFileSize() const {
    if (!HasPlannedTargetFileSize()) {
      return false;
    }
    // The planned target file size should not have been exceeded.
    // TargetWindowWouldExceedSizeLimits() ensures that the advertised size of
    // each target window would not make the target file exceed that limit, and
    // DecodeBody() will return RESULT_ERROR if the actual decoded output ever
    // exceeds the advertised target window size.
    if (total_of_target_window_sizes_ > planned_target_file_size_) {
      VCD_DFATAL << "Internal error: Decoded data size "
                 << total_of_target_window_sizes_
                 << " exceeds planned target file size "
                 << planned_target_file_size_ << VCD_ENDL;
      return true;
    }
    return total_of_target_window_sizes_ == planned_target_file_size_;
  }

  // Checks to see whether adding a new target window of the specified size
  // would exceed the planned target file size, the maximum target file size,
  // or the maximum target window size.  If so, logs an error and returns true;
  // otherwise, returns false.
  bool TargetWindowWouldExceedSizeLimits(size_t window_size) const;

  // Returns the amount of input data passed to the last DecodeChunk()
  // that was not consumed by the decoder.  This is essential if
  // SetPlannedTargetFileSize() is being used, in order to preserve the
  // remaining input data stream once the planned target file has been decoded.
  size_t GetUnconsumedDataSize() const {
    return unparsed_bytes_.size();
  }

  // This function will return true if the decoder has parsed a complete delta
  // file header plus zero or more delta file windows, with no data left over.
  // It will also return true if no delta data at all was decoded.  If these
  // conditions are not met, then FinishDecoding() should not be called.
  bool IsDecodingComplete() const {
    if (!FoundFileHeader()) {
      // No complete delta file header has been parsed yet.  DecodeChunk()
      // may have received some data that it hasn't yet parsed, in which case
      // decoding is incomplete.
      return unparsed_bytes_.empty();
    } else if (custom_code_table_decoder_.get()) {
      // The decoder is in the middle of parsing a custom code table.
      return false;
    } else if (delta_window_.FoundWindowHeader()) {
      // The decoder is in the middle of parsing an interleaved format delta
      // window.
      return false;
    } else if (ReachedPlannedTargetFileSize()) {
      // The decoder found exactly the planned number of bytes.  In this case
      // it is OK for unparsed_bytes_ to be non-empty; it contains the leftover
      // data after the end of the delta file.
      return true;
    } else {
      // No complete delta file window has been parsed yet.  DecodeChunk()
      // may have received some data that it hasn't yet parsed, in which case
      // decoding is incomplete.
      return unparsed_bytes_.empty();
    }
  }

  const char* dictionary_ptr() const { return dictionary_ptr_; }

  size_t dictionary_size() const { return dictionary_size_; }

  VCDiffAddressCache* addr_cache() { return addr_cache_.get(); }

  string* decoded_target() { return &decoded_target_; }

  bool allow_vcd_target() const { return allow_vcd_target_; }

  void SetAllowVcdTarget(bool allow_vcd_target) {
    if (start_decoding_was_called_) {
      VCD_DFATAL << "SetAllowVcdTarget() called after StartDecoding()"
                 << VCD_ENDL;
      return;
    }
    allow_vcd_target_ = allow_vcd_target;
  }

 private:
  // Reads the VCDiff delta file header section as described in RFC section 4.1,
  // except the custom code table data.  Returns RESULT_ERROR if an error
  // occurred, or RESULT_END_OF_DATA if the end of available data was reached
  // before the entire header could be read.  (The latter may be an error
  // condition if there is no more data available.)  Otherwise, advances
  // data->position_ past the header and returns RESULT_SUCCESS.
  //
  VCDiffResult ReadDeltaFileHeader(ParseableChunk* data);

  // Indicates whether or not the header has already been read.
  bool FoundFileHeader() const { return addr_cache_.get() != NULL; }

  // If ReadDeltaFileHeader() finds the VCD_CODETABLE flag set within the delta
  // file header, this function parses the custom cache sizes and initializes
  // a nested VCDiffStreamingDecoderImpl object that will be used to parse the
  // custom code table in ReadCustomCodeTable().  Returns RESULT_ERROR if an
  // error occurred, or RESULT_END_OF_DATA if the end of available data was
  // reached before the custom cache sizes could be read.  Otherwise, returns
  // the number of bytes read.
  //
  int InitCustomCodeTable(const char* data_start, const char* data_end);

  // If a custom code table was specified in the header section that was parsed
  // by ReadDeltaFileHeader(), this function makes a recursive call to another
  // VCDiffStreamingDecoderImpl object (custom_code_table_decoder_), since the
  // custom code table is expected to be supplied as an embedded VCDIFF
  // encoding that uses the standard code table.  Returns RESULT_ERROR if an
  // error occurs, or RESULT_END_OF_DATA if the end of available data was
  // reached before the entire custom code table could be read.  Otherwise,
  // returns RESULT_SUCCESS and sets *data_ptr to the position after the encoded
  // custom code table.  If the function returns RESULT_SUCCESS or
  // RESULT_END_OF_DATA, it advances data->position_ past the parsed bytes.
  //
  VCDiffResult ReadCustomCodeTable(ParseableChunk* data);

  // Called after the decoder exhausts all input data.  This function
  // copies from decoded_target_ into output_string all the data that
  // has not yet been output.  It sets decoded_target_output_position_
  // to mark the start of the next data that needs to be output.
  void AppendNewOutputText(OutputStringInterface* output_string);

  // Appends to output_string the portion of decoded_target_ that has
  // not yet been output, then clears decoded_target_.  This function is
  // called after each complete target window has been decoded if
  // allow_vcd_target is false.  In that case, there is no need to retain
  // target data from any window except the current window.
  void FlushDecodedTarget(OutputStringInterface* output_string);

  // Contents and length of the source (dictionary) data.
  const char* dictionary_ptr_;
  size_t dictionary_size_;

  // This string will be used to store any unparsed bytes left over when
  // DecodeChunk() reaches the end of its input and returns RESULT_END_OF_DATA.
  // It will also be used to concatenate those unparsed bytes with the data
  // supplied to the next call to DecodeChunk(), so that they appear in
  // contiguous memory.
  string unparsed_bytes_;

  // The portion of the target file that has been decoded so far.  This will be
  // used to fill the output string for DecodeChunk(), and will also be used to
  // execute COPY instructions that reference target data.  Since the source
  // window can come from a range of addresses in the previously decoded target
  // data, the entire target file needs to be available to the decoder, not just
  // the current target window.
  string decoded_target_;

  // The VCDIFF version byte (also known as "header4") from the
  // delta file header.
  unsigned char vcdiff_version_code_;

  VCDiffDeltaFileWindow delta_window_;

  std::auto_ptr<VCDiffAddressCache> addr_cache_;

  // Will be NULL unless a custom code table has been defined.
  std::auto_ptr<VCDiffCodeTableData> custom_code_table_;

  // Used to receive the decoded custom code table.
  string custom_code_table_string_;

  // If a custom code table is specified, it will be expressed
  // as an embedded VCDIFF delta file which uses the default code table
  // as the source file (dictionary).  Use a child decoder object
  // to decode that delta file.
  std::auto_ptr<VCDiffStreamingDecoderImpl> custom_code_table_decoder_;

  // If set, then the decoder is expecting *exactly* this number of
  // target bytes to be decoded from one or more delta file windows.
  // If this number is exceeded while decoding a window, but was not met
  // before starting on that window, an error will be reported.
  // If FinishDecoding() is called before this number is met, an error
  // will also be reported.  This feature is used for decoding the
  // embedded code table data within a VCDIFF delta file; we want to
  // stop processing the embedded data once the entire code table has
  // been decoded, and treat the rest of the available data as part
  // of the enclosing delta file.
  size_t planned_target_file_size_;

  size_t maximum_target_file_size_;

  size_t maximum_target_window_size_;

  // Contains the sum of the decoded sizes of all target windows seen so far,
  // including the expected total size of the current target window in progress
  // (even if some of the current target window has not yet been decoded.)
  size_t total_of_target_window_sizes_;

  // Contains the byte position within decoded_target_ of the first data that
  // has not yet been output by AppendNewOutputText().
  size_t decoded_target_output_position_;

  // This value is used to ensure the correct order of calls to the interface
  // functions, i.e., a single call to StartDecoding(), followed by zero or
  // more calls to DecodeChunk(), followed by a single call to
  // FinishDecoding().
  bool start_decoding_was_called_;

  // If this value is true then the VCD_TARGET flag can be specified to allow
  // the source segment to be chosen from the previously-decoded target data.
  // (This is the default behavior.)  If it is false, then specifying the
  // VCD_TARGET flag is considered an error, and the decoder does not need to
  // keep in memory any decoded target data prior to the current window.
  bool allow_vcd_target_;

  // Making these private avoids implicit copy constructor & assignment operator
  VCDiffStreamingDecoderImpl(const VCDiffStreamingDecoderImpl&);  // NOLINT
  void operator=(const VCDiffStreamingDecoderImpl&);
};

// *** Methods for VCDiffStreamingDecoderImpl

const size_t VCDiffStreamingDecoderImpl::kDefaultMaximumTargetFileSize;
const size_t VCDiffStreamingDecoderImpl::kUnlimitedBytes;

VCDiffStreamingDecoderImpl::VCDiffStreamingDecoderImpl()
    : maximum_target_file_size_(kDefaultMaximumTargetFileSize),
      maximum_target_window_size_(kDefaultMaximumTargetFileSize),
      allow_vcd_target_(true) {
  delta_window_.Init(this);
  Reset();
}

// Reset() will delete the component objects without reallocating them.
VCDiffStreamingDecoderImpl::~VCDiffStreamingDecoderImpl() { Reset(); }

void VCDiffStreamingDecoderImpl::Reset() {
  start_decoding_was_called_ = false;
  dictionary_ptr_ = NULL;
  dictionary_size_ = 0;
  vcdiff_version_code_ = '\0';
  planned_target_file_size_ = kUnlimitedBytes;
  total_of_target_window_sizes_ = 0;
  addr_cache_.reset();
  custom_code_table_.reset();
  custom_code_table_decoder_.reset();
  delta_window_.Reset();
  decoded_target_output_position_ = 0;
}

void VCDiffStreamingDecoderImpl::StartDecoding(const char* dictionary_ptr,
                                               size_t dictionary_size) {
  if (start_decoding_was_called_) {
    VCD_DFATAL << "StartDecoding() called twice without FinishDecoding()"
               << VCD_ENDL;
    return;
  }
  unparsed_bytes_.clear();
  decoded_target_.clear();  // delta_window_.Reset() depends on this
  Reset();
  dictionary_ptr_ = dictionary_ptr;
  dictionary_size_ = dictionary_size;
  start_decoding_was_called_ = true;
}

// Reads the VCDiff delta file header section as described in RFC section 4.1:
//
//     Header1                                  - byte = 0xD6 (ASCII 'V' | 0x80)
//     Header2                                  - byte = 0xC3 (ASCII 'C' | 0x80)
//     Header3                                  - byte = 0xC4 (ASCII 'D' | 0x80)
//     Header4                                  - byte
//     Hdr_Indicator                            - byte
//     [Secondary compressor ID]                - byte
//     [Length of code table data]              - integer
//     [Code table data]
//
// Initializes the code table and address cache objects.  Returns RESULT_ERROR
// if an error occurred, and RESULT_END_OF_DATA if the end of available data was
// reached before the entire header could be read.  (The latter may be an error
// condition if there is no more data available.)  Otherwise, returns
// RESULT_SUCCESS, and removes the header bytes from the data string.
//
// It's relatively inefficient to expect this function to parse any number of
// input bytes available, down to 1 byte, but it is necessary in case the input
// is not a properly formatted VCDIFF delta file.  If the entire input consists
// of two bytes "12", then we should recognize that it does not match the
// initial VCDIFF magic number "VCD" and report an error, rather than waiting
// indefinitely for more input that will never arrive.
//
VCDiffResult VCDiffStreamingDecoderImpl::ReadDeltaFileHeader(
    ParseableChunk* data) {
  if (FoundFileHeader()) {
    return RESULT_SUCCESS;
  }
  size_t data_size = data->UnparsedSize();
  const DeltaFileHeader* header =
      reinterpret_cast<const DeltaFileHeader*>(data->UnparsedData());
  bool wrong_magic_number = false;
  switch (data_size) {
    // Verify only the bytes that are available.
    default:
      // Found header contents up to and including VCDIFF version
      vcdiff_version_code_ = header->header4;
      if ((vcdiff_version_code_ != 0x00) &&  // Draft standard VCDIFF (RFC 3284)
          (vcdiff_version_code_ != 'S')) {   // Enhancements for SDCH protocol
        VCD_ERROR << "Unrecognized VCDIFF format version" << VCD_ENDL;
        return RESULT_ERROR;
      }
      // fall through
    case 3:
      if (header->header3 != 0xC4) {  // magic value 'D' | 0x80
        wrong_magic_number = true;
      }
      // fall through
    case 2:
      if (header->header2 != 0xC3) {  // magic value 'C' | 0x80
        wrong_magic_number = true;
      }
      // fall through
    case 1:
      if (header->header1 != 0xD6) {  // magic value 'V' | 0x80
        wrong_magic_number = true;
      }
      // fall through
    case 0:
      if (wrong_magic_number) {
        VCD_ERROR << "Did not find VCDIFF header bytes; "
                      "input is not a VCDIFF delta file" << VCD_ENDL;
        return RESULT_ERROR;
      }
      if (data_size < sizeof(DeltaFileHeader)) return RESULT_END_OF_DATA;
  }
  // Secondary compressor not supported.
  if (header->hdr_indicator & VCD_DECOMPRESS) {
    VCD_ERROR << "Secondary compression is not supported" << VCD_ENDL;
    return RESULT_ERROR;
  }
  if (header->hdr_indicator & VCD_CODETABLE) {
    int bytes_parsed = InitCustomCodeTable(
        data->UnparsedData() + sizeof(DeltaFileHeader),
        data->End());
    switch (bytes_parsed) {
      case RESULT_ERROR:
        return RESULT_ERROR;
      case RESULT_END_OF_DATA:
        return RESULT_END_OF_DATA;
      default:
        data->Advance(sizeof(DeltaFileHeader) + bytes_parsed);
    }
  } else {
    addr_cache_.reset(new VCDiffAddressCache);
    // addr_cache_->Init() will be called
    // from VCDiffStreamingDecoderImpl::DecodeChunk()
    data->Advance(sizeof(DeltaFileHeader));
  }
  return RESULT_SUCCESS;
}

int VCDiffStreamingDecoderImpl::InitCustomCodeTable(const char* data_start,
                                                    const char* data_end) {
  // A custom code table is being specified.  Parse the variable-length
  // cache sizes and begin parsing the encoded custom code table.
  int32_t near_cache_size = 0, same_cache_size = 0;
  VCDiffHeaderParser header_parser(data_start, data_end);
  if (!header_parser.ParseInt32("size of near cache", &near_cache_size)) {
    return header_parser.GetResult();
  }
  if (!header_parser.ParseInt32("size of same cache", &same_cache_size)) {
    return header_parser.GetResult();
  }
  custom_code_table_.reset(new struct VCDiffCodeTableData);
  memset(custom_code_table_.get(), 0, sizeof(struct VCDiffCodeTableData));
  custom_code_table_string_.clear();
  addr_cache_.reset(new VCDiffAddressCache(near_cache_size, same_cache_size));
  // addr_cache_->Init() will be called
  // from VCDiffStreamingDecoderImpl::DecodeChunk()

  // If we reach this point (the start of the custom code table)
  // without encountering a RESULT_END_OF_DATA condition, then we won't call
  // ReadDeltaFileHeader() again for this delta file.
  //
  // Instantiate a recursive decoder to interpret the custom code table
  // as a VCDIFF encoding of the default code table.
  custom_code_table_decoder_.reset(new VCDiffStreamingDecoderImpl);
  custom_code_table_decoder_->StartDecoding(
      reinterpret_cast<const char*>(
          &VCDiffCodeTableData::kDefaultCodeTableData),
      sizeof(VCDiffCodeTableData::kDefaultCodeTableData));
  custom_code_table_decoder_->SetPlannedTargetFileSize(
      sizeof(*custom_code_table_));
  return static_cast<int>(header_parser.ParsedSize());
}

VCDiffResult VCDiffStreamingDecoderImpl::ReadCustomCodeTable(
    ParseableChunk* data) {
  if (!custom_code_table_decoder_.get()) {
    return RESULT_SUCCESS;
  }
  if (!custom_code_table_.get()) {
    VCD_DFATAL << "Internal error:  custom_code_table_decoder_ is set,"
                  " but custom_code_table_ is NULL" << VCD_ENDL;
    return RESULT_ERROR;
  }
  OutputString<string> output_string(&custom_code_table_string_);
  if (!custom_code_table_decoder_->DecodeChunk(data->UnparsedData(),
                                               data->UnparsedSize(),
                                               &output_string)) {
    return RESULT_ERROR;
  }
  if (custom_code_table_string_.length() < sizeof(*custom_code_table_)) {
    // Skip over the consumed data.
    data->Finish();
    return RESULT_END_OF_DATA;
  }
  if (!custom_code_table_decoder_->FinishDecoding()) {
    return RESULT_ERROR;
  }
  if (custom_code_table_string_.length() != sizeof(*custom_code_table_)) {
    VCD_DFATAL << "Decoded custom code table size ("
               << custom_code_table_string_.length()
               << ") does not match size of a code table ("
               << sizeof(*custom_code_table_) << ")" << VCD_ENDL;
    return RESULT_ERROR;
  }
  memcpy(custom_code_table_.get(),
         custom_code_table_string_.data(),
         sizeof(*custom_code_table_));
  custom_code_table_string_.clear();
  // Skip over the consumed data.
  data->FinishExcept(custom_code_table_decoder_->GetUnconsumedDataSize());
  custom_code_table_decoder_.reset();
  delta_window_.UseCodeTable(*custom_code_table_, addr_cache_->LastMode());
  return RESULT_SUCCESS;
}

void VCDiffStreamingDecoderImpl::FlushDecodedTarget(
    OutputStringInterface* output_string) {
  output_string->append(
      decoded_target_.data() + decoded_target_output_position_,
      decoded_target_.size() - decoded_target_output_position_);
  decoded_target_.clear();
  delta_window_.set_target_window_start_pos(0);
  decoded_target_output_position_ = 0;
}

void VCDiffStreamingDecoderImpl::AppendNewOutputText(
    OutputStringInterface* output_string) {
  const size_t bytes_decoded_this_chunk =
      decoded_target_.size() - decoded_target_output_position_;
  if (bytes_decoded_this_chunk > 0) {
    size_t target_bytes_remaining = delta_window_.TargetBytesRemaining();
    if (target_bytes_remaining > 0) {
      // The decoder is midway through decoding a target window.  Resize
      // output_string to match the expected length.  The interface guarantees
      // not to resize output_string more than once per target window decoded.
      output_string->ReserveAdditionalBytes(bytes_decoded_this_chunk
                                            + target_bytes_remaining);
    }
    output_string->append(
        decoded_target_.data() + decoded_target_output_position_,
        bytes_decoded_this_chunk);
    decoded_target_output_position_ = decoded_target_.size();
  }
}

bool VCDiffStreamingDecoderImpl::DecodeChunk(
    const char* data,
    size_t len,
    OutputStringInterface* output_string) {
  if (!start_decoding_was_called_) {
    VCD_DFATAL << "DecodeChunk() called without StartDecoding()" << VCD_ENDL;
    Reset();
    return false;
  }
  ParseableChunk parseable_chunk(data, len);
  if (!unparsed_bytes_.empty()) {
    unparsed_bytes_.append(data, len);
    parseable_chunk.SetDataBuffer(unparsed_bytes_.data(),
                                  unparsed_bytes_.size());
  }
  VCDiffResult result = ReadDeltaFileHeader(&parseable_chunk);
  if (RESULT_SUCCESS == result) {
    result = ReadCustomCodeTable(&parseable_chunk);
  }
  if (RESULT_SUCCESS == result) {
    while (!parseable_chunk.Empty()) {
      result = delta_window_.DecodeWindow(&parseable_chunk);
      if (RESULT_SUCCESS != result) {
        break;
      }
      if (ReachedPlannedTargetFileSize()) {
        // Found exactly the length we expected.  Stop decoding.
        break;
      }
      if (!allow_vcd_target()) {
        // VCD_TARGET will never be used to reference target data before the
        // start of the current window, so flush and clear the contents of
        // decoded_target_.
        FlushDecodedTarget(output_string);
      }
    }
  }
  if (RESULT_ERROR == result) {
    Reset();  // Don't allow further DecodeChunk calls
    return false;
  }
  unparsed_bytes_.assign(parseable_chunk.UnparsedData(),
                         parseable_chunk.UnparsedSize());
  AppendNewOutputText(output_string);
  return true;
}

// Finishes decoding after all data has been received.  Returns true
// if decoding of the entire stream was successful.
bool VCDiffStreamingDecoderImpl::FinishDecoding() {
  bool success = true;
  if (!start_decoding_was_called_) {
    VCD_WARNING << "FinishDecoding() called before StartDecoding(),"
                   " or called after DecodeChunk() returned false"
                << VCD_ENDL;
    success = false;
  } else if (!IsDecodingComplete()) {
    VCD_ERROR << "FinishDecoding() called before parsing entire"
                 " delta file window" << VCD_ENDL;
    success = false;
  }
  // Reset the object state for the next decode operation
  Reset();
  return success;
}

bool VCDiffStreamingDecoderImpl::TargetWindowWouldExceedSizeLimits(
    size_t window_size) const {
  if (window_size > maximum_target_window_size_) {
    VCD_ERROR << "Length of target window (" << window_size
              << ") exceeds limit of " << maximum_target_window_size_
              << " bytes" << VCD_ENDL;
    return true;
  }
  if (HasPlannedTargetFileSize()) {
    // The logical expression to check would be:
    //
    //   total_of_target_window_sizes_ + window_size > planned_target_file_size_
    //
    // but the addition might cause an integer overflow if target_bytes_to_add
    // is very large.  So it is better to check target_bytes_to_add against
    // the remaining planned target bytes.
    size_t remaining_planned_target_file_size =
        planned_target_file_size_ - total_of_target_window_sizes_;
    if (window_size > remaining_planned_target_file_size) {
      VCD_ERROR << "Length of target window (" << window_size
                << " bytes) plus previous windows ("
                << total_of_target_window_sizes_
                << " bytes) would exceed planned size of "
                << planned_target_file_size_ << " bytes" << VCD_ENDL;
      return true;
    }
  }
  size_t remaining_maximum_target_bytes =
      maximum_target_file_size_ - total_of_target_window_sizes_;
  if (window_size > remaining_maximum_target_bytes) {
    VCD_ERROR << "Length of target window (" << window_size
              << " bytes) plus previous windows ("
              << total_of_target_window_sizes_
              << " bytes) would exceed maximum target file size of "
              << maximum_target_file_size_ << " bytes" << VCD_ENDL;
    return true;
  }
  return false;
}

// *** Methods for VCDiffDeltaFileWindow

void VCDiffDeltaFileWindow::Reset() {
  found_header_ = false;

  // Mark the start of the current target window.
  target_window_start_pos_ = parent_ ? parent_->decoded_target()->size() : 0U;
  target_window_length_ = 0;

  source_segment_ptr_ = NULL;
  source_segment_length_ = 0;

  instructions_and_sizes_.Invalidate();
  data_for_add_and_run_.Invalidate();
  addresses_for_copy_.Invalidate();

  interleaved_bytes_expected_ = 0;

  has_checksum_ = false;
  expected_checksum_ = 0;
}

VCDiffResult VCDiffDeltaFileWindow::SetUpWindowSections(
    VCDiffHeaderParser* header_parser) {
  size_t add_and_run_data_length = 0;
  size_t instructions_and_sizes_length = 0;
  size_t addresses_length = 0;
  if (!header_parser->ParseSectionLengths(has_checksum_,
                                          &add_and_run_data_length,
                                          &instructions_and_sizes_length,
                                          &addresses_length,
                                          &expected_checksum_)) {
    return header_parser->GetResult();
  }
  if (parent_->AllowInterleaved() &&
      (add_and_run_data_length == 0) &&
      (addresses_length == 0)) {
    // The interleaved format is being used.
    interleaved_bytes_expected_ =
        static_cast<int>(instructions_and_sizes_length);
    UpdateInterleavedSectionPointers(header_parser->UnparsedData(),
                                     header_parser->End());
  } else {
    // If interleaved format is not used, then the whole window contents
    // must be available before decoding can begin.  If only part of
    // the current window is available, then report end of data
    // and re-parse the whole header when DecodeChunk() is called again.
    if (header_parser->UnparsedSize() < (add_and_run_data_length +
                                         instructions_and_sizes_length +
                                         addresses_length)) {
      return RESULT_END_OF_DATA;
    }
    data_for_add_and_run_.Init(header_parser->UnparsedData(),
                               add_and_run_data_length);
    instructions_and_sizes_.Init(data_for_add_and_run_.End(),
                                 instructions_and_sizes_length);
    addresses_for_copy_.Init(instructions_and_sizes_.End(), addresses_length);
    if (addresses_for_copy_.End() != header_parser->EndOfDeltaWindow()) {
      VCD_ERROR << "The end of the instructions section "
                   "does not match the end of the delta window" << VCD_ENDL;
      return RESULT_ERROR;
    }
  }
  reader_.Init(instructions_and_sizes_.UnparsedDataAddr(),
               instructions_and_sizes_.End());
  return RESULT_SUCCESS;
}

// Here are the elements of the delta window header to be parsed,
// from section 4 of the RFC:
//
//     Window1
//         Win_Indicator                            - byte
//         [Source segment size]                    - integer
//         [Source segment position]                - integer
//         The delta encoding of the target window
//             Length of the delta encoding         - integer
//             The delta encoding
//                 Size of the target window        - integer
//                 Delta_Indicator                  - byte
//                 Length of data for ADDs and RUNs - integer
//                 Length of instructions and sizes - integer
//                 Length of addresses for COPYs    - integer
//                 Data section for ADDs and RUNs   - array of bytes
//                 Instructions and sizes section   - array of bytes
//                 Addresses section for COPYs      - array of bytes
//
VCDiffResult VCDiffDeltaFileWindow::ReadHeader(
    ParseableChunk* parseable_chunk) {
  std::string* decoded_target = parent_->decoded_target();
  VCDiffHeaderParser header_parser(parseable_chunk->UnparsedData(),
                                   parseable_chunk->End());
  size_t source_segment_position = 0;
  unsigned char win_indicator = 0;
  if (!header_parser.ParseWinIndicatorAndSourceSegment(
          parent_->dictionary_size(),
          decoded_target->size(),
          parent_->allow_vcd_target(),
          &win_indicator,
          &source_segment_length_,
          &source_segment_position)) {
    return header_parser.GetResult();
  }
  has_checksum_ = parent_->AllowChecksum() && (win_indicator & VCD_CHECKSUM);
  if (!header_parser.ParseWindowLengths(&target_window_length_)) {
    return header_parser.GetResult();
  }
  if (parent_->TargetWindowWouldExceedSizeLimits(target_window_length_)) {
    // An error has been logged by TargetWindowWouldExceedSizeLimits().
    return RESULT_ERROR;
  }
  header_parser.ParseDeltaIndicator();
  VCDiffResult setup_return_code = SetUpWindowSections(&header_parser);
  if (RESULT_SUCCESS != setup_return_code) {
    return setup_return_code;
  }
  // Reserve enough space in the output string for the current target window.
  const size_t wanted_capacity =
      target_window_start_pos_ + target_window_length_;
  if (decoded_target->capacity() < wanted_capacity) {
    decoded_target->reserve(wanted_capacity);
  }
  // Get a pointer to the start of the source segment.
  if (win_indicator & VCD_SOURCE) {
    source_segment_ptr_ = parent_->dictionary_ptr() + source_segment_position;
  } else if (win_indicator & VCD_TARGET) {
    // This assignment must happen after the reserve().
    // decoded_target should not be resized again while processing this window,
    // so source_segment_ptr_ should remain valid.
    source_segment_ptr_ = decoded_target->data() + source_segment_position;
  }
  // The whole window header was found and parsed successfully.
  found_header_ = true;
  parseable_chunk->Advance(header_parser.ParsedSize());
  parent_->AddToTotalTargetWindowSize(target_window_length_);
  return RESULT_SUCCESS;
}

void VCDiffDeltaFileWindow::UpdateInstructionPointer(
    ParseableChunk* parseable_chunk) {
  if (IsInterleaved()) {
    size_t bytes_parsed = instructions_and_sizes_.ParsedSize();
    // Reduce expected instruction segment length by bytes parsed
    interleaved_bytes_expected_ -= static_cast<int>(bytes_parsed);
    parseable_chunk->Advance(bytes_parsed);
  }
}

inline size_t VCDiffDeltaFileWindow::TargetBytesDecoded() {
  return parent_->decoded_target()->size() - target_window_start_pos_;
}

size_t VCDiffDeltaFileWindow::TargetBytesRemaining() {
  if (target_window_length_ == 0) {
    // There is no window being decoded at present
    return 0;
  } else {
    return target_window_length_ - TargetBytesDecoded();
  }
}

inline void VCDiffDeltaFileWindow::CopyBytes(const char* data, size_t size) {
  parent_->decoded_target()->append(data, size);
}

inline void VCDiffDeltaFileWindow::RunByte(unsigned char byte, size_t size) {
  parent_->decoded_target()->append(size, byte);
}

VCDiffResult VCDiffDeltaFileWindow::DecodeAdd(size_t size) {
  if (size > data_for_add_and_run_.UnparsedSize()) {
    return RESULT_END_OF_DATA;
  }
  // Write the next "size" data bytes
  CopyBytes(data_for_add_and_run_.UnparsedData(), size);
  data_for_add_and_run_.Advance(size);
  return RESULT_SUCCESS;
}

VCDiffResult VCDiffDeltaFileWindow::DecodeRun(size_t size) {
  if (data_for_add_and_run_.Empty()) {
    return RESULT_END_OF_DATA;
  }
  // Write "size" copies of the next data byte
  RunByte(*data_for_add_and_run_.UnparsedData(), size);
  data_for_add_and_run_.Advance(1);
  return RESULT_SUCCESS;
}

VCDiffResult VCDiffDeltaFileWindow::DecodeCopy(size_t size,
                                               unsigned char mode) {
  // Keep track of the number of target bytes decoded as a local variable
  // to avoid recalculating it each time it is needed.
  size_t target_bytes_decoded = TargetBytesDecoded();
  const VCDAddress here_address =
      static_cast<VCDAddress>(source_segment_length_ + target_bytes_decoded);
  const VCDAddress decoded_address = parent_->addr_cache()->DecodeAddress(
      here_address,
      mode,
      addresses_for_copy_.UnparsedDataAddr(),
      addresses_for_copy_.End());
  switch (decoded_address) {
    case RESULT_ERROR:
      VCD_ERROR << "Unable to decode address for COPY" << VCD_ENDL;
      return RESULT_ERROR;
    case RESULT_END_OF_DATA:
      return RESULT_END_OF_DATA;
    default:
      if ((decoded_address < 0) || (decoded_address > here_address)) {
        VCD_DFATAL << "Internal error: unexpected address " << decoded_address
                   << " returned from DecodeAddress, with here_address = "
                   << here_address << VCD_ENDL;
        return RESULT_ERROR;
      }
      break;
  }
  size_t address = static_cast<size_t>(decoded_address);
  if ((address + size) <= source_segment_length_) {
    // Copy all data from source segment
    CopyBytes(&source_segment_ptr_[address], size);
    return RESULT_SUCCESS;
  }
  // Copy some data from target window...
  if (address < source_segment_length_) {
    // ... plus some data from source segment
    const size_t partial_copy_size = source_segment_length_ - address;
    CopyBytes(&source_segment_ptr_[address], partial_copy_size);
    target_bytes_decoded += partial_copy_size;
    address += partial_copy_size;
    size -= partial_copy_size;
  }
  address -= source_segment_length_;
  // address is now based at start of target window
  const char* const target_segment_ptr = parent_->decoded_target()->data() +
                                         target_window_start_pos_;
  while (size > (target_bytes_decoded - address)) {
    // Recursive copy that extends into the yet-to-be-copied target data
    const size_t partial_copy_size = target_bytes_decoded - address;
    CopyBytes(&target_segment_ptr[address], partial_copy_size);
    target_bytes_decoded += partial_copy_size;
    address += partial_copy_size;
    size -= partial_copy_size;
  }
  CopyBytes(&target_segment_ptr[address], size);
  return RESULT_SUCCESS;
}

int VCDiffDeltaFileWindow::DecodeBody(ParseableChunk* parseable_chunk) {
  if (IsInterleaved() && (instructions_and_sizes_.UnparsedData()
                              != parseable_chunk->UnparsedData())) {
    VCD_DFATAL << "Internal error: interleaved format is used, but the"
                  " input pointer does not point to the instructions section"
               << VCD_ENDL;
    return RESULT_ERROR;
  }
  while (TargetBytesDecoded() < target_window_length_) {
    int32_t decoded_size = VCD_INSTRUCTION_ERROR;
    unsigned char mode = 0;
    VCDiffInstructionType instruction =
        reader_.GetNextInstruction(&decoded_size, &mode);
    switch (instruction) {
      case VCD_INSTRUCTION_END_OF_DATA:
        UpdateInstructionPointer(parseable_chunk);
        return RESULT_END_OF_DATA;
      case VCD_INSTRUCTION_ERROR:
        return RESULT_ERROR;
      default:
        break;
    }
    const size_t size = static_cast<size_t>(decoded_size);
    // The value of "size" itself could be enormous (say, INT32_MAX)
    // so check it individually against the limit to protect against
    // overflow when adding it to something else.
    if ((size > target_window_length_) ||
        ((size + TargetBytesDecoded()) > target_window_length_)) {
      VCD_ERROR << VCDiffInstructionName(instruction)
                << " with size " << size
                << " plus existing " << TargetBytesDecoded()
                << " bytes of target data exceeds length of target"
                   " window (" << target_window_length_ << " bytes)"
                << VCD_ENDL;
      return RESULT_ERROR;
    }
    VCDiffResult result = RESULT_SUCCESS;
    switch (instruction) {
      case VCD_ADD:
        result = DecodeAdd(size);
        break;
      case VCD_RUN:
        result = DecodeRun(size);
        break;
      case VCD_COPY:
        result = DecodeCopy(size, mode);
        break;
      default:
        VCD_DFATAL << "Unexpected instruction type " << instruction
                   << "in opcode stream" << VCD_ENDL;
        return RESULT_ERROR;
    }
    switch (result) {
      case RESULT_END_OF_DATA:
        reader_.UnGetInstruction();
        UpdateInstructionPointer(parseable_chunk);
        return RESULT_END_OF_DATA;
      case RESULT_ERROR:
        return RESULT_ERROR;
      case RESULT_SUCCESS:
        break;
    }
  }
  if (TargetBytesDecoded() != target_window_length_) {
    VCD_ERROR << "Decoded target window size (" << TargetBytesDecoded()
              << " bytes) does not match expected size ("
              << target_window_length_ << " bytes)" << VCD_ENDL;
    return RESULT_ERROR;
  }
  const char* const target_window_start =
      parent_->decoded_target()->data() + target_window_start_pos_;
  if (has_checksum_ &&
      (ComputeAdler32(target_window_start, target_window_length_)
           != expected_checksum_)) {
    VCD_ERROR << "Target data does not match checksum; this could mean "
                 "that the wrong dictionary was used" << VCD_ENDL;
    return RESULT_ERROR;
  }
  if (!instructions_and_sizes_.Empty()) {
    VCD_ERROR << "Excess instructions and sizes left over "
                 "after decoding target window" << VCD_ENDL;
      return RESULT_ERROR;
  }
  if (!IsInterleaved()) {
    // Standard format is being used, with three separate sections for the
    // instructions, data, and addresses.
    if (!data_for_add_and_run_.Empty()) {
      VCD_ERROR << "Excess ADD/RUN data left over "
                   "after decoding target window" << VCD_ENDL;
        return RESULT_ERROR;
    }
    if (!addresses_for_copy_.Empty()) {
      VCD_ERROR << "Excess COPY addresses left over "
                   "after decoding target window" << VCD_ENDL;
        return RESULT_ERROR;
    }
    // Reached the end of the window.  Update the ParseableChunk to point to the
    // end of the addresses section, which is the last section in the window.
    parseable_chunk->SetPosition(addresses_for_copy_.End());
  } else {
    // Interleaved format is being used.
    UpdateInstructionPointer(parseable_chunk);
  }
  return RESULT_SUCCESS;
}

VCDiffResult VCDiffDeltaFileWindow::DecodeWindow(
    ParseableChunk* parseable_chunk) {
  if (!parent_) {
    VCD_DFATAL << "Internal error: VCDiffDeltaFileWindow::DecodeWindow() "
                  "called before VCDiffDeltaFileWindow::Init()" << VCD_ENDL;
    return RESULT_ERROR;
  }
  if (!found_header_) {
    switch (ReadHeader(parseable_chunk)) {
      case RESULT_END_OF_DATA:
        return RESULT_END_OF_DATA;
      case RESULT_ERROR:
        return RESULT_ERROR;
      default:
        // Reset address cache between windows (RFC section 5.1)
        if (!parent_->addr_cache()->Init()) {
          VCD_DFATAL << "Error initializing address cache" << VCD_ENDL;
          return RESULT_ERROR;
        }
    }
  } else {
    // We are resuming a window that was partially decoded before a
    // RESULT_END_OF_DATA was returned.  This can only happen on the first
    // loop iteration, and only if the interleaved format is enabled and used.
    if (!IsInterleaved()) {
      VCD_DFATAL << "Internal error: Resumed decoding of a delta file window"
                    " when interleaved format is not being used" << VCD_ENDL;
      return RESULT_ERROR;
    }
    UpdateInterleavedSectionPointers(parseable_chunk->UnparsedData(),
                                     parseable_chunk->End());
    reader_.UpdatePointers(instructions_and_sizes_.UnparsedDataAddr(),
                           instructions_and_sizes_.End());
  }
  switch (DecodeBody(parseable_chunk)) {
    case RESULT_END_OF_DATA:
      if (MoreDataExpected()) {
        return RESULT_END_OF_DATA;
      } else {
        VCD_ERROR << "End of data reached while decoding VCDIFF delta file"
                  << VCD_ENDL;
        // fall through to RESULT_ERROR case
      }
    case RESULT_ERROR:
      return RESULT_ERROR;
    default:
      break;  // DecodeBody succeeded
  }
  // Get ready to read a new delta window
  Reset();
  return RESULT_SUCCESS;
}

// *** Methods for VCDiffStreamingDecoder

VCDiffStreamingDecoder::VCDiffStreamingDecoder()
: impl_(new VCDiffStreamingDecoderImpl) { }

VCDiffStreamingDecoder::~VCDiffStreamingDecoder() { delete impl_; }

void VCDiffStreamingDecoder::StartDecoding(const char* source, size_t len) {
  impl_->StartDecoding(source, len);
}

bool VCDiffStreamingDecoder::DecodeChunkToInterface(
    const char* data,
    size_t len,
    OutputStringInterface* output_string) {
  return impl_->DecodeChunk(data, len, output_string);
}

bool VCDiffStreamingDecoder::FinishDecoding() {
  return impl_->FinishDecoding();
}

bool VCDiffStreamingDecoder::SetMaximumTargetFileSize(
    size_t new_maximum_target_file_size) {
  return impl_->SetMaximumTargetFileSize(new_maximum_target_file_size);
}

bool VCDiffStreamingDecoder::SetMaximumTargetWindowSize(
    size_t new_maximum_target_window_size) {
  return impl_->SetMaximumTargetWindowSize(new_maximum_target_window_size);
}

void VCDiffStreamingDecoder::SetAllowVcdTarget(bool allow_vcd_target) {
  impl_->SetAllowVcdTarget(allow_vcd_target);
}

bool VCDiffDecoder::DecodeToInterface(const char* dictionary_ptr,
                                      size_t dictionary_size,
                                      const string& encoding,
                                      OutputStringInterface* target) {
  target->clear();
  decoder_.StartDecoding(dictionary_ptr, dictionary_size);
  if (!decoder_.DecodeChunkToInterface(encoding.data(),
                                       encoding.size(),
                                       target)) {
    return false;
  }
  return decoder_.FinishDecoding();
}

}  // namespace open_vcdiff
