// 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_HEADERPARSER_H_
#define OPEN_VCDIFF_HEADERPARSER_H_

#include <config.h>
#include <stddef.h>  // NULL
#include <stdint.h>  // int32_t, uint32_t
#include "checksum.h"  // VCDChecksum
#include "vcdiff_defs.h"  // VCDiffResult

namespace open_vcdiff {

// This class contains a contiguous memory buffer with start and end pointers,
// as well as a position pointer which shows how much of the buffer has been
// parsed and how much remains.
//
// Because no virtual destructor is defined for ParseableChunk, a pointer to
// a child class of ParseableChunk must be destroyed using its specific type,
// rather than as a ParseableChunk*.
class ParseableChunk {
 public:
  ParseableChunk(const char* data_start, size_t data_size) {
    SetDataBuffer(data_start, data_size);
  }

  const char* End() const { return end_; }

  // The number of bytes remaining to be parsed.  This is not necessarily the
  // same as the initial size of the buffer; it changes with each call to
  // Advance().
  size_t UnparsedSize() const {
    return end_ - position_;
  }

  // The number of bytes that have already been parsed.
  size_t ParsedSize() const {
    return position_ - start_;
  }

  bool Empty() const { return 0 == UnparsedSize(); }

  // The start of the data remaining to be parsed.
  const char* UnparsedData() const { return position_; }

  // Returns a pointer to the start of the data remaining to be parsed.
  const char** UnparsedDataAddr() { return &position_; }

  // Moves the parsing position forward by number_of_bytes.
  void Advance(size_t number_of_bytes);

  // Jumps the parsing position to a new location.
  void SetPosition(const char* position);

  // Jumps the parsing position to the end of the data chunk.
  void Finish() {
    position_ = end_;
  }

  // Jumps the parsing position so that there are now number_of_bytes
  // bytes left to parse.  This number should be smaller than the size of data
  // to be parsed before the function was called.
  void FinishExcept(size_t number_of_bytes);

  void SetDataBuffer(const char* data_start, size_t data_size) {
    start_ = data_start;
    end_ = data_start + data_size;
    position_ = start_;
  }

 private:
  const char* start_;
  const char* end_;

  // The current parsing position within the data chunk.
  // Must always respect start_ <= position_ <= end_.
  const char* position_;

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

// Represents one of the three sections in the delta window, as described in
// RFC section 4.3:
//     * Data section for ADDs and RUNs
//     * Instructions and sizes section
//     * Addresses section for COPYs
// When using the interleaved format, data and addresses are pulled from the
// instructions and sizes section rather than being stored in separate sections.
// For that reason, this class allows one DeltaWindowSection to be based on
// another, such that the same position pointer is shared by both sections;
// i.e., UnparsedDataAddr() returns the same value for both objects.
// To achieve this end, one extra level of indirection (a pointer to a
// ParseableChunk object) is added.
class DeltaWindowSection {
 public:
  DeltaWindowSection() : parseable_chunk_(NULL), owned_(true) { }

  ~DeltaWindowSection() {
    FreeChunk();
  }

  void Init(const char* data_start, size_t data_size) {
    if (owned_ && parseable_chunk_) {
      // Reuse the already-allocated ParseableChunk object.
      parseable_chunk_->SetDataBuffer(data_start, data_size);
    } else {
      parseable_chunk_ = new ParseableChunk(data_start, data_size);
      owned_ = true;
    }
  }

  void Init(DeltaWindowSection* original) {
    FreeChunk();
    parseable_chunk_ = original->parseable_chunk_;
    owned_ = false;
  }

  void Invalidate() { FreeChunk(); }

  bool IsOwned() const { return owned_; }

  // The following functions just pass their arguments to the underlying
  // ParseableChunk object.

  const char* End() const {
    return parseable_chunk_->End();
  }

  size_t UnparsedSize() const {
    return parseable_chunk_->UnparsedSize();
  }

  size_t ParsedSize() const {
    return parseable_chunk_->ParsedSize();
  }

  bool Empty() const {
    return parseable_chunk_->Empty();
  }

  const char* UnparsedData() const {
    return parseable_chunk_->UnparsedData();
  }

  const char** UnparsedDataAddr() {
    return parseable_chunk_->UnparsedDataAddr();
  }

  void Advance(size_t number_of_bytes) {
    return parseable_chunk_->Advance(number_of_bytes);
  }
 private:
  void FreeChunk() {
    if (owned_) {
      delete parseable_chunk_;
    }
    parseable_chunk_ = NULL;
  }

  // Will be NULL until Init() has been called.  If owned_ is true, this will
  // point to a ParseableChunk object that has been allocated with "new" and
  // must be deleted by this DeltaWindowSection object.  If owned_ is false,
  // this points at the parseable_chunk_ owned by a different DeltaWindowSection
  // object.  In this case, it is important to free the DeltaWindowSection which
  // does not own the ParseableChunk before (or simultaneously to) freeing the
  // DeltaWindowSection that owns it, or else deleted memory may be accessed.
  ParseableChunk* parseable_chunk_;
  bool owned_;

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

// Used to parse the bytes and Varints that make up the delta file header
// or delta window header.
class VCDiffHeaderParser {
 public:
  // header_start should be the start of the header to be parsed;
  // data_end is the position just after the last byte of available data
  // (which may extend far past the end of the header.)
  VCDiffHeaderParser(const char* header_start, const char* data_end);

  // One of these functions should be called for each element of the header.
  // variable_description is a description of the value that we are attempting
  // to parse, and will only be used to create descriptive error messages.
  // If the function returns true, then the element was parsed successfully
  // and its value has been placed in *value.  If the function returns false,
  // then *value is unchanged, and GetResult() can be called to return the
  // reason that the element could not be parsed, which will be either
  // RESULT_ERROR (an error occurred), or RESULT_END_OF_DATA (the limit data_end
  // was reached before the end of the element to be parsed.)  Once one of these
  // functions has returned false, further calls to any of the Parse...
  // functions will also return false without performing any additional actions.
  // Typical usage is as follows:
  //     int32_t segment_length = 0;
  //     if (!header_parser.ParseInt32("segment length", &segment_length)) {
  //       return header_parser.GetResult();
  //     }
  //
  // The following example takes advantage of the fact that calling a Parse...
  // function after an error or end-of-data condition is legal and does nothing.
  // It can thus parse more than one element in a row and check the status
  // afterwards.  If the first call to ParseInt32() fails, the second will have
  // no effect:
  //
  //     int32_t segment_length = 0, segment_position = 0;
  //     header_parser.ParseInt32("segment length", &segment_length));
  //     header_parser.ParseInt32("segment position", &segment_position));
  //     if (RESULT_SUCCESS != header_parser.GetResult()) {
  //       return header_parser.GetResult();
  //     }
  //
  bool ParseByte(unsigned char* value);
  bool ParseInt32(const char* variable_description, int32_t* value);
  bool ParseUInt32(const char* variable_description, uint32_t* value);
  bool ParseChecksum(const char* variable_description, VCDChecksum* value);
  bool ParseSize(const char* variable_description, size_t* value);

  // Parses the first three elements of the delta window header:
  //
  //     Win_Indicator                            - byte
  //     [Source segment size]                    - integer (VarintBE format)
  //     [Source segment position]                - integer (VarintBE format)
  //
  // Returns true if the values were parsed successfully and the values were
  // found to be acceptable.  Returns false otherwise, in which case
  // GetResult() can be called to return the reason that the two values
  // could not be validated.  This will be either RESULT_ERROR (an error
  // occurred and was logged), or RESULT_END_OF_DATA (the limit data_end was
  // reached before the end of the values to be parsed.)  If return value is
  // true, then *win_indicator, *source_segment_length, and
  // *source_segment_position are populated with the parsed values.  Otherwise,
  // the values of these output arguments are undefined.
  //
  // dictionary_size: The size of the dictionary (source) file.  Used to
  //     validate the limits of source_segment_length and
  //     source_segment_position if the source segment is taken from the
  //     dictionary (i.e., if the parsed *win_indicator equals VCD_SOURCE.)
  // decoded_target_size: The size of the target data that has been decoded
  //     so far, including all target windows.  Used to validate the limits of
  //     source_segment_length and source_segment_position if the source segment
  //     is taken from the target (i.e., if the parsed *win_indicator equals
  //     VCD_TARGET.)
  // allow_vcd_target: If this argument is false, and the parsed *win_indicator
  //     is VCD_TARGET, then an error is produced; if true, VCD_TARGET is
  //     allowed.
  // win_indicator (output): Points to a single unsigned char (not an array)
  //     that will receive the parsed value of Win_Indicator.
  // source_segment_length (output): The parsed length of the source segment.
  // source_segment_position (output): The parsed zero-based index in the
  //     source/target file from which the source segment is to be taken.
  bool ParseWinIndicatorAndSourceSegment(size_t dictionary_size,
                                         size_t decoded_target_size,
                                         bool allow_vcd_target,
                                         unsigned char* win_indicator,
                                         size_t* source_segment_length,
                                         size_t* source_segment_position);

  // Parses the following two elements of the delta window header:
  //
  //     Length of the delta encoding             - integer (VarintBE format)
  //     Size of the target window                - integer (VarintBE format)
  //
  // Return conditions and values are the same as for
  // ParseWinIndicatorAndSourceSegment(), above.
  //
  bool ParseWindowLengths(size_t* target_window_length);

  // May only be called after ParseWindowLengths() has returned RESULT_SUCCESS.
  // Returns a pointer to the end of the delta window (which might not point to
  // a valid memory location if there is insufficient input data.)
  //
  const char* EndOfDeltaWindow() const;

  // Parses the following element of the delta window header:
  //
  //     Delta_Indicator                          - byte
  //
  // Because none of the bits in Delta_Indicator are used by this implementation
  // of VCDIFF, this function does not have an output argument to return the
  // value of that field.  It may return RESULT_SUCCESS, RESULT_ERROR, or
  // RESULT_END_OF_DATA as with the other Parse...() functions.
  //
  bool ParseDeltaIndicator();

  // Parses the following 3 elements of the delta window header:
  //
  //     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)
  //
  // Return conditions and values are the same as for
  // ParseWinIndicatorAndSourceSegment(), above.
  //
  bool ParseSectionLengths(bool has_checksum,
                           size_t* add_and_run_data_length,
                           size_t* instructions_and_sizes_length,
                           size_t* addresses_length,
                           VCDChecksum* checksum);

  // If one of the Parse... functions returned false, this function
  // can be used to find the result code (RESULT_ERROR or RESULT_END_OF_DATA)
  // describing the reason for the most recent parse failure.  If none of the
  // Parse... functions has returned false, returns RESULT_SUCCESS.
  VCDiffResult GetResult() const {
    return return_code_;
  }

  // The following functions just pass their arguments to the underlying
  // ParseableChunk object.

  const char* End() const {
    return parseable_chunk_.End();
  }

  size_t UnparsedSize() const {
    return parseable_chunk_.UnparsedSize();
  }

  size_t ParsedSize() const {
    return parseable_chunk_.ParsedSize();
  }

  const char* UnparsedData() const {
    return parseable_chunk_.UnparsedData();
  }

 private:
  // Parses two variable-length integers representing the source segment length
  // and source segment position (== offset.)  Checks whether the source segment
  // length and position would cause it to exceed the size of the source file or
  // target file.  Returns true if the values were parsed successfully and the
  // values were found to be acceptable.  Returns false otherwise, in which case
  // GetResult() can be called to return the reason that the two values could
  // not be validated, which will be either RESULT_ERROR (an error occurred and
  // was logged), or RESULT_END_OF_DATA (the limit data_end was reached before
  // the end of the integers to be parsed.)
  // from_size: The requested size of the source segment.
  // from_boundary_name: A NULL-terminated string naming the end of the
  //     source or target file, used in error messages.
  // from_name: A NULL-terminated string naming the source or target file,
  //     also used in error messages.
  // source_segment_length (output): The parsed length of the source segment.
  // source_segment_position (output): The parsed zero-based index in the
  //     source/target file from which the source segment is to be taken.
  //
  bool ParseSourceSegmentLengthAndPosition(size_t from_size,
                                           const char* from_boundary_name,
                                           const char* from_name,
                                           size_t* source_segment_length,
                                           size_t* source_segment_position);

  ParseableChunk parseable_chunk_;

  // Contains the result code of the last Parse...() operation that failed
  // (RESULT_ERROR or RESULT_END_OF_DATA).  If no Parse...() method has been
  // called, or if all calls to Parse...() were successful, then this contains
  // RESULT_SUCCESS.
  VCDiffResult return_code_;

  // Will be zero until ParseWindowLengths() has been called.  After
  // ParseWindowLengths() has been called successfully, this contains the
  // parsed length of the delta encoding.
  size_t delta_encoding_length_;

  // Will be NULL until ParseWindowLengths() has been called.  After
  // ParseWindowLengths() has been called successfully, this points to the
  // beginning of the section of the current window titled "The delta encoding"
  // in the RFC, i.e., to the position just after the length of the delta
  // encoding.
  const char* delta_encoding_start_;

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

}  // namespace open_vcdiff

#endif  // OPEN_VCDIFF_HEADERPARSER_H_
