// 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.

#ifndef OPEN_VCDIFF_VCDECODER_H_
#define OPEN_VCDIFF_VCDECODER_H_

#include <stddef.h>  // size_t
#include <string>
#include "google/output_string.h"

namespace open_vcdiff {

class VCDiffStreamingDecoderImpl;

// A streaming decoder class.  Takes a dictionary (source) file and a delta
// file, and produces the original target file.  It is intended to process
// the partial contents of the delta file as they arrive, in "chunks".
// As soon as a chunk of bytes is received from a file read or from a network
// transmission, it can be passed to DecodeChunk(), which will then output
// as much of the target file as it can.
//
// The client should use this class as follows:
//    VCDiffStreamingDecoder v;
//    v.StartDecoding(dictionary_ptr, dictionary_size);
//    while (any data left) {
//      if (!v.DecodeChunk(data, len, &output_string)) {
//        handle error;
//        break;
//      }
//      process(output_string);  // might have no new data, though
//    }
//    if (!v.FinishDecoding()) { ... handle error ... }
//
// I.e., the allowed pattern of calls is
//    StartDecoding DecodeChunk* FinishDecoding
//
// NOTE: It is not necessary to call FinishDecoding if DecodeChunk
//       returns false.  When DecodeChunk returns false to signal an
//       error, it resets its state and is ready for a new StartDecoding.
//       If FinishDecoding is called, it will also return false.
//
class VCDiffStreamingDecoder {
 public:
  VCDiffStreamingDecoder();
  ~VCDiffStreamingDecoder();

  // Resets the dictionary contents to "dictionary_ptr[0,dictionary_size-1]"
  // and sets up the data structures for decoding.  Note that the dictionary
  // contents are not copied, and the client is responsible for ensuring that
  // dictionary_ptr is valid until FinishDecoding is called.
  //
  void StartDecoding(const char* dictionary_ptr, size_t dictionary_size);

  // Accepts "data[0,len-1]" as additional data received in the
  // compressed stream.  If any chunks of data can be fully decoded,
  // they are appended to output_string.
  //
  // Returns true on success, and false if the data was malformed
  // or if there was an error in decoding it (e.g. out of memory, etc.).
  //
  // Note: we *append*, so the old contents of output_string stick around.
  // This convention differs from the non-streaming Encode/Decode
  // interfaces in VCDiffDecoder.
  //
  // output_string is guaranteed to be resized no more than once for each
  // window in the VCDIFF delta file.  This rule is irrespective
  // of the number of calls to DecodeChunk().
  //
  template<class OutputType>
  bool DecodeChunk(const char* data, size_t len, OutputType* output) {
    OutputString<OutputType> output_string(output);
    return DecodeChunkToInterface(data, len, &output_string);
  }

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

  // Finishes decoding after all data has been received.  Returns true
  // if decoding of the entire stream was successful.  FinishDecoding()
  // must be called for the current target before StartDecoding() can be
  // called for a different target.
  //
  bool FinishDecoding();

  // *** Adjustable parameters ***

  // Specifies the maximum allowable target file size.  If the decoder
  // encounters a delta file that would cause it to create a target file larger
  // than this limit, it will log an error and stop decoding.  If the decoder is
  // applied to delta files whose sizes vary greatly and whose contents can be
  // trusted, then a value larger than the the default value (64 MB) can be
  // specified to allow for maximum flexibility.  On the other hand, if the
  // input data is known never to exceed a particular size, and/or the input
  // data may be maliciously constructed, a lower value can be supplied in order
  // to guard against running out of memory or swapping to disk while decoding
  // an extremely large target file.  The argument must be between 0 and
  // INT32_MAX (2G); if it is within these bounds, the function will set the
  // limit and return true.  Otherwise, the function will return false and will
  // not change the limit.  Setting the limit to 0 will cause all decode
  // operations of non-empty target files to fail.
  bool SetMaximumTargetFileSize(size_t new_maximum_target_file_size);

  // Specifies the maximum allowable target *window* size.  (A target file is
  // composed of zero or more target windows.)  If the decoder encounters a
  // delta window that would cause it to create a target window larger
  // than this limit, it will log an error and stop decoding.
  bool SetMaximumTargetWindowSize(size_t new_maximum_target_window_size);

  // This interface must be called before StartDecoding().  If its argument
  // 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.
  void SetAllowVcdTarget(bool allow_vcd_target);

 private:
  VCDiffStreamingDecoderImpl* const impl_;

  // Make the copy constructor and assignment operator private
  // so that they don't inadvertently get used.
  explicit VCDiffStreamingDecoder(const VCDiffStreamingDecoder&);
  void operator=(const VCDiffStreamingDecoder&);
};

// A simpler (non-streaming) interface to the VCDIFF decoder that can be used
// if the entire delta file is available.
//
class VCDiffDecoder {
 public:
  typedef std::string string;

  VCDiffDecoder() { }
  ~VCDiffDecoder() { }

  /***** Simple interface *****/

  // Replaces old contents of "*target" with the result of decoding
  // the bytes found in "encoding."
  //
  // Returns true if "encoding" was a well-formed sequence of
  // instructions, and returns false if not.
  //
  template<class OutputType>
  bool Decode(const char* dictionary_ptr,
              size_t dictionary_size,
              const string& encoding,
              OutputType* target) {
    OutputString<OutputType> output_string(target);
    return DecodeToInterface(dictionary_ptr,
                             dictionary_size,
                             encoding,
                             &output_string);
  }

 private:
  bool DecodeToInterface(const char* dictionary_ptr,
                         size_t dictionary_size,
                         const string& encoding,
                         OutputStringInterface* target);

  VCDiffStreamingDecoder decoder_;

  // Make the copy constructor and assignment operator private
  // so that they don't inadvertently get used.
  explicit VCDiffDecoder(const VCDiffDecoder&);
  void operator=(const VCDiffDecoder&);
};

};  // namespace open_vcdiff

#endif  // OPEN_VCDIFF_VCDECODER_H_