// Parquet reader amalgamation

#include "parquet-extension.hpp"
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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.
 */

// clang-format off

#ifndef _THRIFT_TRANSPORT_PLATFORM_SOCKET_H_
#  define _THRIFT_TRANSPORT_PLATFORM_SOCKET_H_

#ifdef _WIN32
#  include <winsock2.h>
#  define THRIFT_GET_SOCKET_ERROR ::WSAGetLastError()
#  define THRIFT_ERRNO (*_errno())
#  define THRIFT_EINPROGRESS WSAEINPROGRESS
#  define THRIFT_EAGAIN WSAEWOULDBLOCK
#  define THRIFT_EINTR WSAEINTR
#  define THRIFT_ECONNRESET WSAECONNRESET
#  define THRIFT_ENOTCONN WSAENOTCONN
#  define THRIFT_ETIMEDOUT WSAETIMEDOUT
#  define THRIFT_EWOULDBLOCK WSAEWOULDBLOCK
#  define THRIFT_EPIPE WSAECONNRESET
#  define THRIFT_NO_SOCKET_CACHING SO_EXCLUSIVEADDRUSE
#  define THRIFT_SOCKET SOCKET
#  define THRIFT_INVALID_SOCKET INVALID_SOCKET
#  define THRIFT_SOCKETPAIR thrift_socketpair
#  define THRIFT_FCNTL thrift_fcntl
#  define THRIFT_O_NONBLOCK 1
#  define THRIFT_F_GETFL 0
#  define THRIFT_F_SETFL 1
#  define THRIFT_GETTIMEOFDAY thrift_gettimeofday
#  define THRIFT_CLOSESOCKET closesocket
#  define THRIFT_CLOSE _close
#  define THRIFT_OPEN _open
#  define THRIFT_FTRUNCATE _chsize_s
#  define THRIFT_FSYNC _commit
#  define THRIFT_LSEEK _lseek
#  define THRIFT_WRITE _write
#  define THRIFT_READ _read
#  define THRIFT_IOCTL_SOCKET ioctlsocket
#  define THRIFT_IOCTL_SOCKET_NUM_BYTES_TYPE u_long
#  define THRIFT_FSTAT _fstat
#  define THRIFT_STAT _stat
#  ifdef _WIN32_WCE
#    define THRIFT_GAI_STRERROR(...) thrift_wstr2str(gai_strerrorW(__VA_ARGS__))
#  else
#    define THRIFT_GAI_STRERROR gai_strerrorA
#  endif
#  define THRIFT_SSIZET ptrdiff_t
#  if (_MSC_VER < 1900)
#    define THRIFT_SNPRINTF _snprintf
#  else
#    define THRIFT_SNPRINTF snprintf
#  endif
#  define THRIFT_SLEEP_SEC thrift_sleep
#  define THRIFT_SLEEP_USEC thrift_usleep
#  define THRIFT_TIMESPEC thrift_timespec
#  define THRIFT_CTIME_R thrift_ctime_r
#  define THRIFT_POLL thrift_poll
#  if WINVER <= 0x0502 //XP, Server2003
#    define THRIFT_POLLFD  thrift_pollfd
#    define THRIFT_POLLIN  0x0300
#    define THRIFT_POLLOUT 0x0010
#  else //Vista, Win7...
#    define THRIFT_POLLFD  pollfd
#    define THRIFT_POLLIN  POLLIN
#    define THRIFT_POLLOUT POLLOUT
#  endif //WINVER
#  define THRIFT_SHUT_RDWR SD_BOTH
#  if !defined(AI_ADDRCONFIG)
#    define AI_ADDRCONFIG 0x00000400
#  endif
#else //not _WIN32
#  include <errno.h>
#  define THRIFT_GET_SOCKET_ERROR errno
#  define THRIFT_ERRNO errno
#  define THRIFT_EINTR       EINTR
#  define THRIFT_EINPROGRESS EINPROGRESS
#  define THRIFT_ECONNRESET  ECONNRESET
#  define THRIFT_ENOTCONN    ENOTCONN
#  define THRIFT_ETIMEDOUT   ETIMEDOUT
#  define THRIFT_EWOULDBLOCK EWOULDBLOCK
#  define THRIFT_EAGAIN      EAGAIN
#  define THRIFT_EPIPE       EPIPE
#  define THRIFT_NO_SOCKET_CACHING SO_REUSEADDR
#  define THRIFT_SOCKET int
#  define THRIFT_INVALID_SOCKET (-1)
#  define THRIFT_SOCKETPAIR socketpair
#  define THRIFT_FCNTL fcntl
#  define THRIFT_O_NONBLOCK O_NONBLOCK
#  define THRIFT_F_GETFL F_GETFL
#  define THRIFT_F_SETFL F_SETFL
#  define THRIFT_GETTIMEOFDAY gettimeofday
#  define THRIFT_CLOSESOCKET close
#  define THRIFT_CLOSE close
#  define THRIFT_OPEN open
#  define THRIFT_FTRUNCATE ftruncate
#  define THRIFT_FSYNC fsync
#  define THRIFT_LSEEK lseek
#  define THRIFT_WRITE write
#  define THRIFT_READ read
#  define THRIFT_IOCTL_SOCKET ioctl
#  define THRIFT_IOCTL_SOCKET_NUM_BYTES_TYPE int
#  define THRIFT_STAT stat
#  define THRIFT_FSTAT fstat
#  define THRIFT_GAI_STRERROR gai_strerror
#  define THRIFT_SSIZET ssize_t
#  define THRIFT_SNPRINTF snprintf
#  define THRIFT_SLEEP_SEC sleep
#  define THRIFT_SLEEP_USEC usleep
#  define THRIFT_TIMESPEC timespec
#  define THRIFT_CTIME_R ctime_r
#  define THRIFT_POLL poll
#  define THRIFT_POLLFD  pollfd
#  define THRIFT_POLLIN  POLLIN
#  define THRIFT_POLLOUT POLLOUT
#  define THRIFT_SHUT_RDWR SHUT_RDWR
#endif

#endif // _THRIFT_TRANSPORT_PLATFORM_SOCKET_H_

#define SIGNED_RIGHT_SHIFT_IS  1
#define ARITHMETIC_RIGHT_SHIFT 1
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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.
 */

#ifdef _WIN32
//#include <thrift/windows/config.h>
#else
// #include <thrift/config.h>
#endif

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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 _THRIFT_THRIFT_H_
#define _THRIFT_THRIFT_H_ 1

// #include <thrift/transport/PlatformSocket.h>

// #include <thrift/thrift-config.h>

#include <stdio.h>
#include <assert.h>

#include <sys/types.h>
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_INTTYPES_H
#include <inttypes.h>
#endif
#include <string>
#include <map>
#include <list>
#include <set>
#include <vector>
#include <exception>
#include <typeinfo>

// #include <thrift/TLogging.h>
//#include <thrift/TOutput.h>

#define THRIFT_UNUSED_VARIABLE(x) ((void)(x))

namespace apache {
namespace thrift {

class TEnumIterator
    : public std::iterator<std::forward_iterator_tag, std::pair<int, const char*> > {
public:
  TEnumIterator(int n, int* enums, const char** names)
    : ii_(0), n_(n), enums_(enums), names_(names) {}

  int operator++() { return ++ii_; }

  bool operator!=(const TEnumIterator& end) {
    THRIFT_UNUSED_VARIABLE(end);
    assert(end.n_ == -1);
    return (ii_ != n_);
  }

  std::pair<int, const char*> operator*() const { return std::make_pair(enums_[ii_], names_[ii_]); }

private:
  int ii_;
  const int n_;
  int* enums_;
  const char** names_;
};

class TException : public std::exception {
public:
  TException() : message_() {}

  TException(const std::string& message) : message_(message) {}

  ~TException() noexcept override = default;

  const char* what() const noexcept override {
    if (message_.empty()) {
      return "Default TException.";
    } else {
      return message_.c_str();
    }
  }

protected:
  std::string message_;
};

class TDelayedException {
public:
  template <class E>
  static TDelayedException* delayException(const E& e);
  virtual void throw_it() = 0;
  virtual ~TDelayedException() = default;
};

template <class E>
class TExceptionWrapper : public TDelayedException {
public:
  TExceptionWrapper(const E& e) : e_(e) {}
  void throw_it() override {
    E temp(e_);
    delete this;
    throw temp;
  }

private:
  E e_;
};

template <class E>
TDelayedException* TDelayedException::delayException(const E& e) {
  return new TExceptionWrapper<E>(e);
}

#if T_GLOBAL_DEBUG_VIRTUAL > 1
void profile_virtual_call(const std::type_info& info);
void profile_generic_protocol(const std::type_info& template_type, const std::type_info& prot_type);
void profile_print_info(FILE* f);
void profile_print_info();
void profile_write_pprof(FILE* gen_calls_f, FILE* virtual_calls_f);
#endif
}
} // apache::thrift

#endif // #ifndef _THRIFT_THRIFT_H_

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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 _THRIFT_TLOGGING_H_
#define _THRIFT_TLOGGING_H_ 1

// #include <thrift/thrift-config.h>

/**
 * Contains utility macros for debugging and logging.
 *
 */

#include <time.h>

#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif

/**
 * T_GLOBAL_DEBUGGING_LEVEL = 0: all debugging turned off, debug macros undefined
 * T_GLOBAL_DEBUGGING_LEVEL = 1: all debugging turned on
 */
#define T_GLOBAL_DEBUGGING_LEVEL 0

/**
 * T_GLOBAL_LOGGING_LEVEL = 0: all logging turned off, logging macros undefined
 * T_GLOBAL_LOGGING_LEVEL = 1: all logging turned on
 */
#define T_GLOBAL_LOGGING_LEVEL 0

/**
 * Standard wrapper around fprintf what will prefix the file name and line
 * number to the line. Uses T_GLOBAL_DEBUGGING_LEVEL to control whether it is
 * turned on or off.
 *
 * @param format_string
 */
#if T_GLOBAL_DEBUGGING_LEVEL > 0
#define T_DEBUG(format_string, ...)                                                                \
  if (T_GLOBAL_DEBUGGING_LEVEL > 0) {                                                              \
    fprintf(stderr, "[%s,%d] " format_string " \n", __FILE__, __LINE__, ##__VA_ARGS__);            \
  }
#else
#define T_DEBUG(format_string, ...)
#endif

/**
 * analogous to T_DEBUG but also prints the time
 *
 * @param string  format_string input: printf style format string
 */
#if T_GLOBAL_DEBUGGING_LEVEL > 0
#define T_DEBUG_T(format_string, ...)                                                              \
  {                                                                                                \
    if (T_GLOBAL_DEBUGGING_LEVEL > 0) {                                                            \
      time_t now;                                                                                  \
      char dbgtime[26];                                                                            \
      time(&now);                                                                                  \
      THRIFT_CTIME_R(&now, dbgtime);                                                               \
      dbgtime[24] = '\0';                                                                          \
      fprintf(stderr,                                                                              \
              "[%s,%d] [%s] " format_string " \n",                                                 \
              __FILE__,                                                                            \
              __LINE__,                                                                            \
              dbgtime,                                                                             \
              ##__VA_ARGS__);                                                                      \
    }                                                                                              \
  }
#else
#define T_DEBUG_T(format_string, ...)
#endif





/**
 * Log input message
 *
 * @param string  format_string input: printf style format string
 */
#if T_GLOBAL_LOGGING_LEVEL > 0
#define T_LOG_OPER(format_string, ...)                                                             \
  {                                                                                                \
    if (T_GLOBAL_LOGGING_LEVEL > 0) {                                                              \
      time_t now;                                                                                  \
      char dbgtime[26];                                                                            \
      time(&now);                                                                                  \
      THRIFT_CTIME_R(&now, dbgtime);                                                               \
      dbgtime[24] = '\0';                                                                          \
      fprintf(stderr, "[%s] " format_string " \n", dbgtime, ##__VA_ARGS__);                        \
    }                                                                                              \
  }
#else
#define T_LOG_OPER(format_string, ...)
#endif

/**
 * T_GLOBAL_DEBUG_VIRTUAL = 0 or unset: normal operation,
 *                                      virtual call debug messages disabled
 * T_GLOBAL_DEBUG_VIRTUAL = 1:          log a debug messages whenever an
 *                                      avoidable virtual call is made
 * T_GLOBAL_DEBUG_VIRTUAL = 2:          record detailed info that can be
 *                                      printed by calling
 *                                      apache::thrift::profile_print_info()
 */
#if T_GLOBAL_DEBUG_VIRTUAL > 1
#define T_VIRTUAL_CALL() ::apache::thrift::profile_virtual_call(typeid(*this))
#define T_GENERIC_PROTOCOL(template_class, generic_prot, specific_prot)                            \
  do {                                                                                             \
    if (!(specific_prot)) {                                                                        \
      ::apache::thrift::profile_generic_protocol(typeid(*template_class), typeid(*generic_prot));  \
    }                                                                                              \
  } while (0)
#elif T_GLOBAL_DEBUG_VIRTUAL == 1
#define T_VIRTUAL_CALL() fprintf(stderr, "[%s,%d] virtual call\n", __FILE__, __LINE__)
#define T_GENERIC_PROTOCOL(template_class, generic_prot, specific_prot)                            \
  do {                                                                                             \
    if (!(specific_prot)) {                                                                        \
      fprintf(stderr, "[%s,%d] failed to cast to specific protocol type\n", __FILE__, __LINE__);   \
    }                                                                                              \
  } while (0)
#else
#define T_VIRTUAL_CALL()
#define T_GENERIC_PROTOCOL(template_class, generic_prot, specific_prot)
#endif

#endif // #ifndef _THRIFT_TLOGGING_H_

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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 _THRIFT_TRANSPORT_TTRANSPORTEXCEPTION_H_
#define _THRIFT_TRANSPORT_TTRANSPORTEXCEPTION_H_ 1

// FUCK OFF #include <boost/numeric/conversion/cast.hpp>
#include <string>
// #include <thrift/Thrift.h>

namespace apache {
namespace thrift {
namespace transport {

/**
 * Class to encapsulate all the possible types of transport errors that may
 * occur in various transport systems. This provides a sort of generic
 * wrapper around the vague UNIX E_ error codes that lets a common code
 * base of error handling to be used for various types of transports, i.e.
 * pipes etc.
 *
 */
class TTransportException : public apache::thrift::TException {
public:
  /**
   * Error codes for the various types of exceptions.
   */
  enum TTransportExceptionType {
    UNKNOWN = 0,
    NOT_OPEN = 1,
    TIMED_OUT = 2,
    END_OF_FILE = 3,
    INTERRUPTED = 4,
    BAD_ARGS = 5,
    CORRUPTED_DATA = 6,
    INTERNAL_ERROR = 7
  };

  TTransportException() : apache::thrift::TException(), type_(UNKNOWN) {}

  TTransportException(TTransportExceptionType type) : apache::thrift::TException(), type_(type) {}

  TTransportException(const std::string& message)
    : apache::thrift::TException(message), type_(UNKNOWN) {}

  TTransportException(TTransportExceptionType type, const std::string& message)
    : apache::thrift::TException(message), type_(type) {}

  TTransportException(TTransportExceptionType type, const std::string& message, int errno_copy)
    : apache::thrift::TException(message), type_(type) {}

  ~TTransportException() noexcept override = default;

  /**
   * Returns an error code that provides information about the type of error
   * that has occurred.
   *
   * @return Error code
   */
  TTransportExceptionType getType() const noexcept { return type_; }

  const char* what() const noexcept override;

protected:
  /** Just like strerror_r but returns a C++ string object. */
  std::string strerror_s(int errno_copy);

  /** Error code */
  TTransportExceptionType type_;
};

///**
// * Legacy code in transport implementations have overflow issues
// * that need to be enforced.
// */
//template <typename To, typename From> To safe_numeric_cast(From i) {
//  try {
//    return boost::numeric_cast<To>(i);
//  }
//  catch (const std::bad_cast& bc) {
//    throw TTransportException(TTransportException::CORRUPTED_DATA,
//                              bc.what());
//  }
//}

}
}
} // apache::thrift::transport

#endif // #ifndef _THRIFT_TRANSPORT_TTRANSPORTEXCEPTION_H_

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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 _THRIFT_TRANSPORT_TTRANSPORT_H_
#define _THRIFT_TRANSPORT_TTRANSPORT_H_ 1

// #include <thrift/Thrift.h>
// #include <thrift/transport/TTransportException.h>
#include <memory>
#include <string>

namespace apache {
namespace thrift {
namespace transport {

/**
 * Helper template to hoist readAll implementation out of TTransport
 */
template <class Transport_>
uint32_t readAll(Transport_& trans, uint8_t* buf, uint32_t len) {
  uint32_t have = 0;
  uint32_t get = 0;

  while (have < len) {
    get = trans.read(buf + have, len - have);
    if (get <= 0) {
      throw TTransportException(TTransportException::END_OF_FILE, "No more data to read.");
    }
    have += get;
  }

  return have;
}

/**
 * Generic interface for a method of transporting data. A TTransport may be
 * capable of either reading or writing, but not necessarily both.
 *
 */
class TTransport {
public:
  /**
   * Virtual deconstructor.
   */
  virtual ~TTransport() = default;

  /**
   * Whether this transport is open.
   */
  virtual bool isOpen() const { return false; }

  /**
   * Tests whether there is more data to read or if the remote side is
   * still open. By default this is true whenever the transport is open,
   * but implementations should add logic to test for this condition where
   * possible (i.e. on a socket).
   * This is used by a server to check if it should listen for another
   * request.
   */
  virtual bool peek() { return isOpen(); }

  /**
   * Opens the transport for communications.
   *
   * @return bool Whether the transport was successfully opened
   * @throws TTransportException if opening failed
   */
  virtual void open() {
    throw TTransportException(TTransportException::NOT_OPEN, "Cannot open base TTransport.");
  }

  /**
   * Closes the transport.
   */
  virtual void close() {
    throw TTransportException(TTransportException::NOT_OPEN, "Cannot close base TTransport.");
  }

  /**
   * Attempt to read up to the specified number of bytes into the string.
   *
   * @param buf  Reference to the location to write the data
   * @param len  How many bytes to read
   * @return How many bytes were actually read
   * @throws TTransportException If an error occurs
   */
  uint32_t read(uint8_t* buf, uint32_t len) {
    T_VIRTUAL_CALL();
    return read_virt(buf, len);
  }
  virtual uint32_t read_virt(uint8_t* /* buf */, uint32_t /* len */) {
    throw TTransportException(TTransportException::NOT_OPEN, "Base TTransport cannot read.");
  }

  /**
   * Reads the given amount of data in its entirety no matter what.
   *
   * @param s     Reference to location for read data
   * @param len   How many bytes to read
   * @return How many bytes read, which must be equal to size
   * @throws TTransportException If insufficient data was read
   */
  uint32_t readAll(uint8_t* buf, uint32_t len) {
    T_VIRTUAL_CALL();
    return readAll_virt(buf, len);
  }
  virtual uint32_t readAll_virt(uint8_t* buf, uint32_t len) {
    return apache::thrift::transport::readAll(*this, buf, len);
  }

  /**
   * Called when read is completed.
   * This can be over-ridden to perform a transport-specific action
   * e.g. logging the request to a file
   *
   * @return number of bytes read if available, 0 otherwise.
   */
  virtual uint32_t readEnd() {
    // default behaviour is to do nothing
    return 0;
  }

  /**
   * Writes the string in its entirety to the buffer.
   *
   * Note: You must call flush() to ensure the data is actually written,
   * and available to be read back in the future.  Destroying a TTransport
   * object does not automatically flush pending data--if you destroy a
   * TTransport object with written but unflushed data, that data may be
   * discarded.
   *
   * @param buf  The data to write out
   * @throws TTransportException if an error occurs
   */
  void write(const uint8_t* buf, uint32_t len) {
    T_VIRTUAL_CALL();
    write_virt(buf, len);
  }
  virtual void write_virt(const uint8_t* /* buf */, uint32_t /* len */) {
    throw TTransportException(TTransportException::NOT_OPEN, "Base TTransport cannot write.");
  }

  /**
   * Called when write is completed.
   * This can be over-ridden to perform a transport-specific action
   * at the end of a request.
   *
   * @return number of bytes written if available, 0 otherwise
   */
  virtual uint32_t writeEnd() {
    // default behaviour is to do nothing
    return 0;
  }

  /**
   * Flushes any pending data to be written. Typically used with buffered
   * transport mechanisms.
   *
   * @throws TTransportException if an error occurs
   */
  virtual void flush() {
    // default behaviour is to do nothing
  }

  /**
   * Attempts to return a pointer to \c len bytes, possibly copied into \c buf.
   * Does not consume the bytes read (i.e.: a later read will return the same
   * data).  This method is meant to support protocols that need to read
   * variable-length fields.  They can attempt to borrow the maximum amount of
   * data that they will need, then consume (see next method) what they
   * actually use.  Some transports will not support this method and others
   * will fail occasionally, so protocols must be prepared to use read if
   * borrow fails.
   *
   * @oaram buf  A buffer where the data can be stored if needed.
   *             If borrow doesn't return buf, then the contents of
   *             buf after the call are undefined.  This parameter may be
   *             NULL to indicate that the caller is not supplying storage,
   *             but would like a pointer into an internal buffer, if
   *             available.
   * @param len  *len should initially contain the number of bytes to borrow.
   *             If borrow succeeds, *len will contain the number of bytes
   *             available in the returned pointer.  This will be at least
   *             what was requested, but may be more if borrow returns
   *             a pointer to an internal buffer, rather than buf.
   *             If borrow fails, the contents of *len are undefined.
   * @return If the borrow succeeds, return a pointer to the borrowed data.
   *         This might be equal to \c buf, or it might be a pointer into
   *         the transport's internal buffers.
   * @throws TTransportException if an error occurs
   */
  const uint8_t* borrow(uint8_t* buf, uint32_t* len) {
    T_VIRTUAL_CALL();
    return borrow_virt(buf, len);
  }
  virtual const uint8_t* borrow_virt(uint8_t* /* buf */, uint32_t* /* len */) { return nullptr; }

  /**
   * Remove len bytes from the transport.  This should always follow a borrow
   * of at least len bytes, and should always succeed.
   * TODO(dreiss): Is there any transport that could borrow but fail to
   * consume, or that would require a buffer to dump the consumed data?
   *
   * @param len  How many bytes to consume
   * @throws TTransportException If an error occurs
   */
  void consume(uint32_t len) {
    T_VIRTUAL_CALL();
    consume_virt(len);
  }
  virtual void consume_virt(uint32_t /* len */) {
    throw TTransportException(TTransportException::NOT_OPEN, "Base TTransport cannot consume.");
  }

  /**
   * Returns the origin of the transports call. The value depends on the
   * transport used. An IP based transport for example will return the
   * IP address of the client making the request.
   * If the transport doesn't know the origin Unknown is returned.
   *
   * The returned value can be used in a log message for example
   */
  virtual const std::string getOrigin() const { return "Unknown"; }

protected:
  /**
   * Simple constructor.
   */
  TTransport() = default;
};

/**
 * Generic factory class to make an input and output transport out of a
 * source transport. Commonly used inside servers to make input and output
 * streams out of raw clients.
 *
 */
class TTransportFactory {
public:
  TTransportFactory() = default;

  virtual ~TTransportFactory() = default;

  /**
   * Default implementation does nothing, just returns the transport given.
   */
  virtual std::shared_ptr<TTransport> getTransport(std::shared_ptr<TTransport> trans) {
    return trans;
  }
};
}
}
} // apache::thrift::transport

#endif // #ifndef _THRIFT_TRANSPORT_TTRANSPORT_H_

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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 _THRIFT_PROTOCOL_TPROTOCOLEXCEPTION_H_
#define _THRIFT_PROTOCOL_TPROTOCOLEXCEPTION_H_ 1

#include <string>

namespace apache {
namespace thrift {
namespace protocol {

/**
 * Class to encapsulate all the possible types of protocol errors that may
 * occur in various protocol systems. This provides a sort of generic
 * wrapper around the vague UNIX E_ error codes that lets a common code
 * base of error handling to be used for various types of protocols, i.e.
 * pipes etc.
 *
 */
class TProtocolException : public apache::thrift::TException {
public:
  /**
   * Error codes for the various types of exceptions.
   */
  enum TProtocolExceptionType {
    UNKNOWN = 0,
    INVALID_DATA = 1,
    NEGATIVE_SIZE = 2,
    SIZE_LIMIT = 3,
    BAD_VERSION = 4,
    NOT_IMPLEMENTED = 5,
    DEPTH_LIMIT = 6
  };

  TProtocolException() : apache::thrift::TException(), type_(UNKNOWN) {}

  TProtocolException(TProtocolExceptionType type) : apache::thrift::TException(), type_(type) {}

  TProtocolException(const std::string& message)
    : apache::thrift::TException(message), type_(UNKNOWN) {}

  TProtocolException(TProtocolExceptionType type, const std::string& message)
    : apache::thrift::TException(message), type_(type) {}

  ~TProtocolException() noexcept override = default;

  /**
   * Returns an error code that provides information about the type of error
   * that has occurred.
   *
   * @return Error code
   */
  TProtocolExceptionType getType() const { return type_; }

  const char* what() const noexcept override {
    if (message_.empty()) {
      switch (type_) {
      case UNKNOWN:
        return "TProtocolException: Unknown protocol exception";
      case INVALID_DATA:
        return "TProtocolException: Invalid data";
      case NEGATIVE_SIZE:
        return "TProtocolException: Negative size";
      case SIZE_LIMIT:
        return "TProtocolException: Exceeded size limit";
      case BAD_VERSION:
        return "TProtocolException: Invalid version";
      case NOT_IMPLEMENTED:
        return "TProtocolException: Not implemented";
      default:
        return "TProtocolException: (Invalid exception type)";
      }
    } else {
      return message_.c_str();
    }
  }

protected:
  /**
   * Error code
   */
  TProtocolExceptionType type_;
};
}
}
} // apache::thrift::protocol

#endif // #ifndef _THRIFT_PROTOCOL_TPROTOCOLEXCEPTION_H_

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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 _THRIFT_PROTOCOL_TPROTOCOL_H_
#define _THRIFT_PROTOCOL_TPROTOCOL_H_ 1

#ifdef _WIN32
// Need to come before any Windows.h includes
#include <Winsock2.h>
#endif

// #include <thrift/transport/TTransport.h>
// #include <thrift/protocol/TProtocolException.h>

#include <memory>

#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#include <sys/types.h>
#include <string>
#include <map>
#include <vector>
#include <climits>

// Use this to get around strict aliasing rules.
// For example, uint64_t i = bitwise_cast<uint64_t>(returns_double());
// The most obvious implementation is to just cast a pointer,
// but that doesn't work.
// For a pretty in-depth explanation of the problem, see
// http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html
template <typename To, typename From>
static inline To bitwise_cast(From from) {
  static_assert(sizeof(From) == sizeof(To), "sizeof(From) == sizeof(To)");

  // BAD!!!  These are all broken with -O2.
  //return *reinterpret_cast<To*>(&from);  // BAD!!!
  //return *static_cast<To*>(static_cast<void*>(&from));  // BAD!!!
  //return *(To*)(void*)&from;  // BAD!!!

  // Super clean and paritally blessed by section 3.9 of the standard.
  //unsigned char c[sizeof(from)];
  //memcpy(c, &from, sizeof(from));
  //To to;
  //memcpy(&to, c, sizeof(c));
  //return to;

  // Slightly more questionable.
  // Same code emitted by GCC.
  //To to;
  //memcpy(&to, &from, sizeof(from));
  //return to;

  // Technically undefined, but almost universally supported,
  // and the most efficient implementation.
  union {
    From f;
    To t;
  } u;
  u.f = from;
  return u.t;
}


#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif

#ifndef __THRIFT_BYTE_ORDER
# if defined(BYTE_ORDER) && defined(LITTLE_ENDIAN) && defined(BIG_ENDIAN)
#  define __THRIFT_BYTE_ORDER BYTE_ORDER
#  define __THRIFT_LITTLE_ENDIAN LITTLE_ENDIAN
#  define __THRIFT_BIG_ENDIAN BIG_ENDIAN
# else
//#  include <boost/predef/other/endian.h>
#  if BOOST_ENDIAN_BIG_BYTE
#    define __THRIFT_BYTE_ORDER 4321
#    define __THRIFT_LITTLE_ENDIAN 0
#    define __THRIFT_BIG_ENDIAN __THRIFT_BYTE_ORDER
#  elif BOOST_ENDIAN_LITTLE_BYTE
#    define __THRIFT_BYTE_ORDER 1234
#    define __THRIFT_LITTLE_ENDIAN __THRIFT_BYTE_ORDER
#    define __THRIFT_BIG_ENDIAN 0
#  endif
#  ifdef BOOST_LITTLE_ENDIAN
#  else
#  endif
# endif
#endif

#if __THRIFT_BYTE_ORDER == __THRIFT_BIG_ENDIAN
# if !defined(THRIFT_ntohll)
#  define THRIFT_ntohll(n) (n)
#  define THRIFT_htonll(n) (n)
# endif
# if defined(__GNUC__) && defined(__GLIBC__)
#  include <byteswap.h>
#  define THRIFT_htolell(n) bswap_64(n)
#  define THRIFT_letohll(n) bswap_64(n)
#  define THRIFT_htolel(n) bswap_32(n)
#  define THRIFT_letohl(n) bswap_32(n)
#  define THRIFT_htoles(n) bswap_16(n)
#  define THRIFT_letohs(n) bswap_16(n)
# else /* GNUC & GLIBC */
#  define bswap_64(n) \
      ( (((n) & 0xff00000000000000ull) >> 56) \
      | (((n) & 0x00ff000000000000ull) >> 40) \
      | (((n) & 0x0000ff0000000000ull) >> 24) \
      | (((n) & 0x000000ff00000000ull) >> 8)  \
      | (((n) & 0x00000000ff000000ull) << 8)  \
      | (((n) & 0x0000000000ff0000ull) << 24) \
      | (((n) & 0x000000000000ff00ull) << 40) \
      | (((n) & 0x00000000000000ffull) << 56) )
#  define bswap_32(n) \
      ( (((n) & 0xff000000ul) >> 24) \
      | (((n) & 0x00ff0000ul) >> 8)  \
      | (((n) & 0x0000ff00ul) << 8)  \
      | (((n) & 0x000000fful) << 24) )
#  define bswap_16(n) \
      ( (((n) & ((unsigned short)0xff00ul)) >> 8)  \
      | (((n) & ((unsigned short)0x00fful)) << 8)  )
#  define THRIFT_htolell(n) bswap_64(n)
#  define THRIFT_letohll(n) bswap_64(n)
#  define THRIFT_htolel(n) bswap_32(n)
#  define THRIFT_letohl(n) bswap_32(n)
#  define THRIFT_htoles(n) bswap_16(n)
#  define THRIFT_letohs(n) bswap_16(n)
# endif /* GNUC & GLIBC */
#elif __THRIFT_BYTE_ORDER == __THRIFT_LITTLE_ENDIAN
#  define THRIFT_htolell(n) (n)
#  define THRIFT_letohll(n) (n)
#  define THRIFT_htolel(n) (n)
#  define THRIFT_letohl(n) (n)
#  define THRIFT_htoles(n) (n)
#  define THRIFT_letohs(n) (n)
# if defined(__GNUC__) && defined(__GLIBC__)
#  include <byteswap.h>
#  define THRIFT_ntohll(n) bswap_64(n)
#  define THRIFT_htonll(n) bswap_64(n)
# elif defined(_MSC_VER) /* Microsoft Visual C++ */
#  define THRIFT_ntohll(n) ( _byteswap_uint64((uint64_t)n) )
#  define THRIFT_htonll(n) ( _byteswap_uint64((uint64_t)n) )
# elif !defined(THRIFT_ntohll) /* Not GNUC/GLIBC or MSVC */
#  define THRIFT_ntohll(n) ( (((uint64_t)ntohl((uint32_t)n)) << 32) + ntohl((uint32_t)(n >> 32)) )
#  define THRIFT_htonll(n) ( (((uint64_t)htonl((uint32_t)n)) << 32) + htonl((uint32_t)(n >> 32)) )
# endif /* GNUC/GLIBC or MSVC or something else */
#else /* __THRIFT_BYTE_ORDER */
# error "Can't define THRIFT_htonll or THRIFT_ntohll!"
#endif

namespace apache {
namespace thrift {
namespace protocol {

using apache::thrift::transport::TTransport;

/**
 * Enumerated definition of the types that the Thrift protocol supports.
 * Take special note of the T_END type which is used specifically to mark
 * the end of a sequence of fields.
 */
enum TType {
  T_STOP       = 0,
  T_VOID       = 1,
  T_BOOL       = 2,
  T_BYTE       = 3,
  T_I08        = 3,
  T_I16        = 6,
  T_I32        = 8,
  T_U64        = 9,
  T_I64        = 10,
  T_DOUBLE     = 4,
  T_STRING     = 11,
  T_UTF7       = 11,
  T_STRUCT     = 12,
  T_MAP        = 13,
  T_SET        = 14,
  T_LIST       = 15,
  T_UTF8       = 16,
  T_UTF16      = 17
};

/**
 * Enumerated definition of the message types that the Thrift protocol
 * supports.
 */
enum TMessageType {
  T_CALL       = 1,
  T_REPLY      = 2,
  T_EXCEPTION  = 3,
  T_ONEWAY     = 4
};

static const uint32_t DEFAULT_RECURSION_LIMIT = 64;

/**
 * Abstract class for a thrift protocol driver. These are all the methods that
 * a protocol must implement. Essentially, there must be some way of reading
 * and writing all the base types, plus a mechanism for writing out structs
 * with indexed fields.
 *
 * TProtocol objects should not be shared across multiple encoding contexts,
 * as they may need to maintain internal state in some protocols (i.e. XML).
 * Note that is is acceptable for the TProtocol module to do its own internal
 * buffered reads/writes to the underlying TTransport where appropriate (i.e.
 * when parsing an input XML stream, reading should be batched rather than
 * looking ahead character by character for a close tag).
 *
 */
class TProtocol {
public:
  virtual ~TProtocol();

  /**
   * Writing functions.
   */

  virtual uint32_t writeMessageBegin_virt(const std::string& name,
                                          const TMessageType messageType,
                                          const int32_t seqid) = 0;

  virtual uint32_t writeMessageEnd_virt() = 0;

  virtual uint32_t writeStructBegin_virt(const char* name) = 0;

  virtual uint32_t writeStructEnd_virt() = 0;

  virtual uint32_t writeFieldBegin_virt(const char* name,
                                        const TType fieldType,
                                        const int16_t fieldId) = 0;

  virtual uint32_t writeFieldEnd_virt() = 0;

  virtual uint32_t writeFieldStop_virt() = 0;

  virtual uint32_t writeMapBegin_virt(const TType keyType, const TType valType, const uint32_t size)
      = 0;

  virtual uint32_t writeMapEnd_virt() = 0;

  virtual uint32_t writeListBegin_virt(const TType elemType, const uint32_t size) = 0;

  virtual uint32_t writeListEnd_virt() = 0;

  virtual uint32_t writeSetBegin_virt(const TType elemType, const uint32_t size) = 0;

  virtual uint32_t writeSetEnd_virt() = 0;

  virtual uint32_t writeBool_virt(const bool value) = 0;

  virtual uint32_t writeByte_virt(const int8_t byte) = 0;

  virtual uint32_t writeI16_virt(const int16_t i16) = 0;

  virtual uint32_t writeI32_virt(const int32_t i32) = 0;

  virtual uint32_t writeI64_virt(const int64_t i64) = 0;

  virtual uint32_t writeDouble_virt(const double dub) = 0;

  virtual uint32_t writeString_virt(const std::string& str) = 0;

  virtual uint32_t writeBinary_virt(const std::string& str) = 0;

  uint32_t writeMessageBegin(const std::string& name,
                             const TMessageType messageType,
                             const int32_t seqid) {
    T_VIRTUAL_CALL();
    return writeMessageBegin_virt(name, messageType, seqid);
  }

  uint32_t writeMessageEnd() {
    T_VIRTUAL_CALL();
    return writeMessageEnd_virt();
  }

  uint32_t writeStructBegin(const char* name) {
    T_VIRTUAL_CALL();
    return writeStructBegin_virt(name);
  }

  uint32_t writeStructEnd() {
    T_VIRTUAL_CALL();
    return writeStructEnd_virt();
  }

  uint32_t writeFieldBegin(const char* name, const TType fieldType, const int16_t fieldId) {
    T_VIRTUAL_CALL();
    return writeFieldBegin_virt(name, fieldType, fieldId);
  }

  uint32_t writeFieldEnd() {
    T_VIRTUAL_CALL();
    return writeFieldEnd_virt();
  }

  uint32_t writeFieldStop() {
    T_VIRTUAL_CALL();
    return writeFieldStop_virt();
  }

  uint32_t writeMapBegin(const TType keyType, const TType valType, const uint32_t size) {
    T_VIRTUAL_CALL();
    return writeMapBegin_virt(keyType, valType, size);
  }

  uint32_t writeMapEnd() {
    T_VIRTUAL_CALL();
    return writeMapEnd_virt();
  }

  uint32_t writeListBegin(const TType elemType, const uint32_t size) {
    T_VIRTUAL_CALL();
    return writeListBegin_virt(elemType, size);
  }

  uint32_t writeListEnd() {
    T_VIRTUAL_CALL();
    return writeListEnd_virt();
  }

  uint32_t writeSetBegin(const TType elemType, const uint32_t size) {
    T_VIRTUAL_CALL();
    return writeSetBegin_virt(elemType, size);
  }

  uint32_t writeSetEnd() {
    T_VIRTUAL_CALL();
    return writeSetEnd_virt();
  }

  uint32_t writeBool(const bool value) {
    T_VIRTUAL_CALL();
    return writeBool_virt(value);
  }

  uint32_t writeByte(const int8_t byte) {
    T_VIRTUAL_CALL();
    return writeByte_virt(byte);
  }

  uint32_t writeI16(const int16_t i16) {
    T_VIRTUAL_CALL();
    return writeI16_virt(i16);
  }

  uint32_t writeI32(const int32_t i32) {
    T_VIRTUAL_CALL();
    return writeI32_virt(i32);
  }

  uint32_t writeI64(const int64_t i64) {
    T_VIRTUAL_CALL();
    return writeI64_virt(i64);
  }

  uint32_t writeDouble(const double dub) {
    T_VIRTUAL_CALL();
    return writeDouble_virt(dub);
  }

  uint32_t writeString(const std::string& str) {
    T_VIRTUAL_CALL();
    return writeString_virt(str);
  }

  uint32_t writeBinary(const std::string& str) {
    T_VIRTUAL_CALL();
    return writeBinary_virt(str);
  }

  /**
   * Reading functions
   */

  virtual uint32_t readMessageBegin_virt(std::string& name,
                                         TMessageType& messageType,
                                         int32_t& seqid) = 0;

  virtual uint32_t readMessageEnd_virt() = 0;

  virtual uint32_t readStructBegin_virt(std::string& name) = 0;

  virtual uint32_t readStructEnd_virt() = 0;

  virtual uint32_t readFieldBegin_virt(std::string& name, TType& fieldType, int16_t& fieldId) = 0;

  virtual uint32_t readFieldEnd_virt() = 0;

  virtual uint32_t readMapBegin_virt(TType& keyType, TType& valType, uint32_t& size) = 0;

  virtual uint32_t readMapEnd_virt() = 0;

  virtual uint32_t readListBegin_virt(TType& elemType, uint32_t& size) = 0;

  virtual uint32_t readListEnd_virt() = 0;

  virtual uint32_t readSetBegin_virt(TType& elemType, uint32_t& size) = 0;

  virtual uint32_t readSetEnd_virt() = 0;

  virtual uint32_t readBool_virt(bool& value) = 0;

  virtual uint32_t readBool_virt(std::vector<bool>::reference value) = 0;

  virtual uint32_t readByte_virt(int8_t& byte) = 0;

  virtual uint32_t readI16_virt(int16_t& i16) = 0;

  virtual uint32_t readI32_virt(int32_t& i32) = 0;

  virtual uint32_t readI64_virt(int64_t& i64) = 0;

  virtual uint32_t readDouble_virt(double& dub) = 0;

  virtual uint32_t readString_virt(std::string& str) = 0;

  virtual uint32_t readBinary_virt(std::string& str) = 0;

  uint32_t readMessageBegin(std::string& name, TMessageType& messageType, int32_t& seqid) {
    T_VIRTUAL_CALL();
    return readMessageBegin_virt(name, messageType, seqid);
  }

  uint32_t readMessageEnd() {
    T_VIRTUAL_CALL();
    return readMessageEnd_virt();
  }

  uint32_t readStructBegin(std::string& name) {
    T_VIRTUAL_CALL();
    return readStructBegin_virt(name);
  }

  uint32_t readStructEnd() {
    T_VIRTUAL_CALL();
    return readStructEnd_virt();
  }

  uint32_t readFieldBegin(std::string& name, TType& fieldType, int16_t& fieldId) {
    T_VIRTUAL_CALL();
    return readFieldBegin_virt(name, fieldType, fieldId);
  }

  uint32_t readFieldEnd() {
    T_VIRTUAL_CALL();
    return readFieldEnd_virt();
  }

  uint32_t readMapBegin(TType& keyType, TType& valType, uint32_t& size) {
    T_VIRTUAL_CALL();
    return readMapBegin_virt(keyType, valType, size);
  }

  uint32_t readMapEnd() {
    T_VIRTUAL_CALL();
    return readMapEnd_virt();
  }

  uint32_t readListBegin(TType& elemType, uint32_t& size) {
    T_VIRTUAL_CALL();
    return readListBegin_virt(elemType, size);
  }

  uint32_t readListEnd() {
    T_VIRTUAL_CALL();
    return readListEnd_virt();
  }

  uint32_t readSetBegin(TType& elemType, uint32_t& size) {
    T_VIRTUAL_CALL();
    return readSetBegin_virt(elemType, size);
  }

  uint32_t readSetEnd() {
    T_VIRTUAL_CALL();
    return readSetEnd_virt();
  }

  uint32_t readBool(bool& value) {
    T_VIRTUAL_CALL();
    return readBool_virt(value);
  }

  uint32_t readByte(int8_t& byte) {
    T_VIRTUAL_CALL();
    return readByte_virt(byte);
  }

  uint32_t readI16(int16_t& i16) {
    T_VIRTUAL_CALL();
    return readI16_virt(i16);
  }

  uint32_t readI32(int32_t& i32) {
    T_VIRTUAL_CALL();
    return readI32_virt(i32);
  }

  uint32_t readI64(int64_t& i64) {
    T_VIRTUAL_CALL();
    return readI64_virt(i64);
  }

  uint32_t readDouble(double& dub) {
    T_VIRTUAL_CALL();
    return readDouble_virt(dub);
  }

  uint32_t readString(std::string& str) {
    T_VIRTUAL_CALL();
    return readString_virt(str);
  }

  uint32_t readBinary(std::string& str) {
    T_VIRTUAL_CALL();
    return readBinary_virt(str);
  }

  /*
   * std::vector is specialized for bool, and its elements are individual bits
   * rather than bools.   We need to define a different version of readBool()
   * to work with std::vector<bool>.
   */
  uint32_t readBool(std::vector<bool>::reference value) {
    T_VIRTUAL_CALL();
    return readBool_virt(value);
  }

  /**
   * Method to arbitrarily skip over data.
   */
  uint32_t skip(TType type) {
    T_VIRTUAL_CALL();
    return skip_virt(type);
  }
  virtual uint32_t skip_virt(TType type);

  inline std::shared_ptr<TTransport> getTransport() { return ptrans_; }

  // TODO: remove these two calls, they are for backwards
  // compatibility
  inline std::shared_ptr<TTransport> getInputTransport() { return ptrans_; }
  inline std::shared_ptr<TTransport> getOutputTransport() { return ptrans_; }

  // input and output recursion depth are kept separate so that one protocol
  // can be used concurrently for both input and output.
  void incrementInputRecursionDepth() {
    if (recursion_limit_ < ++input_recursion_depth_) {
      throw TProtocolException(TProtocolException::DEPTH_LIMIT);
    }
  }
  void decrementInputRecursionDepth() { --input_recursion_depth_; }

  void incrementOutputRecursionDepth() {
    if (recursion_limit_ < ++output_recursion_depth_) {
      throw TProtocolException(TProtocolException::DEPTH_LIMIT);
    }
  }
  void decrementOutputRecursionDepth() { --output_recursion_depth_; }

  uint32_t getRecursionLimit() const {return recursion_limit_;}
  void setRecurisionLimit(uint32_t depth) {recursion_limit_ = depth;}

protected:
  TProtocol(std::shared_ptr<TTransport> ptrans)
    : ptrans_(ptrans), input_recursion_depth_(0), output_recursion_depth_(0), recursion_limit_(DEFAULT_RECURSION_LIMIT)
  {}

  std::shared_ptr<TTransport> ptrans_;

private:
  TProtocol() = default;
  uint32_t input_recursion_depth_;
  uint32_t output_recursion_depth_;
  uint32_t recursion_limit_;
};

/**
 * Constructs input and output protocol objects given transports.
 */
class TProtocolFactory {
public:
  TProtocolFactory() = default;

  virtual ~TProtocolFactory();

  virtual std::shared_ptr<TProtocol> getProtocol(std::shared_ptr<TTransport> trans) = 0;
  virtual std::shared_ptr<TProtocol> getProtocol(std::shared_ptr<TTransport> inTrans,
               std::shared_ptr<TTransport> outTrans) {
    (void)outTrans;
    return getProtocol(inTrans);
  }
};

/**
 * Dummy protocol class.
 *
 * This class does nothing, and should never be instantiated.
 * It is used only by the generator code.
 */
class TDummyProtocol : public TProtocol {};


// HM: this is sub-optimal since it creates a depencency even for memory-only struct
//// This is the default / legacy choice
//struct TNetworkBigEndian
//{
//  static uint16_t toWire16(uint16_t x)   {return htons(x);}
//  static uint32_t toWire32(uint32_t x)   {return htonl(x);}
//  static uint64_t toWire64(uint64_t x)   {return THRIFT_htonll(x);}
//  static uint16_t fromWire16(uint16_t x) {return ntohs(x);}
//  static uint32_t fromWire32(uint32_t x) {return ntohl(x);}
//  static uint64_t fromWire64(uint64_t x) {return THRIFT_ntohll(x);}
//};
//
//// On most systems, this will be a bit faster than TNetworkBigEndian
//struct TNetworkLittleEndian
//{
//  static uint16_t toWire16(uint16_t x)   {return THRIFT_htoles(x);}
//  static uint32_t toWire32(uint32_t x)   {return THRIFT_htolel(x);}
//  static uint64_t toWire64(uint64_t x)   {return THRIFT_htolell(x);}
//  static uint16_t fromWire16(uint16_t x) {return THRIFT_letohs(x);}
//  static uint32_t fromWire32(uint32_t x) {return THRIFT_letohl(x);}
//  static uint64_t fromWire64(uint64_t x) {return THRIFT_letohll(x);}
//};

struct TOutputRecursionTracker {
  TProtocol &prot_;
  TOutputRecursionTracker(TProtocol &prot) : prot_(prot) {
    prot_.incrementOutputRecursionDepth();
  }
  ~TOutputRecursionTracker() {
    prot_.decrementOutputRecursionDepth();
  }
};

struct TInputRecursionTracker {
  TProtocol &prot_;
  TInputRecursionTracker(TProtocol &prot) : prot_(prot) {
    prot_.incrementInputRecursionDepth();
  }
  ~TInputRecursionTracker() {
    prot_.decrementInputRecursionDepth();
  }
};

/**
 * Helper template for implementing TProtocol::skip().
 *
 * Templatized to avoid having to make virtual function calls.
 */
template <class Protocol_>
uint32_t skip(Protocol_& prot, TType type) {
  TInputRecursionTracker tracker(prot);

  switch (type) {
  case T_BOOL: {
    bool boolv;
    return prot.readBool(boolv);
  }
  case T_BYTE: {
    int8_t bytev = 0;
    return prot.readByte(bytev);
  }
  case T_I16: {
    int16_t i16;
    return prot.readI16(i16);
  }
  case T_I32: {
    int32_t i32;
    return prot.readI32(i32);
  }
  case T_I64: {
    int64_t i64;
    return prot.readI64(i64);
  }
  case T_DOUBLE: {
    double dub;
    return prot.readDouble(dub);
  }
  case T_STRING: {
    std::string str;
    return prot.readBinary(str);
  }
  case T_STRUCT: {
    uint32_t result = 0;
    std::string name;
    int16_t fid;
    TType ftype;
    result += prot.readStructBegin(name);
    while (true) {
      result += prot.readFieldBegin(name, ftype, fid);
      if (ftype == T_STOP) {
        break;
      }
      result += skip(prot, ftype);
      result += prot.readFieldEnd();
    }
    result += prot.readStructEnd();
    return result;
  }
  case T_MAP: {
    uint32_t result = 0;
    TType keyType;
    TType valType;
    uint32_t i, size;
    result += prot.readMapBegin(keyType, valType, size);
    for (i = 0; i < size; i++) {
      result += skip(prot, keyType);
      result += skip(prot, valType);
    }
    result += prot.readMapEnd();
    return result;
  }
  case T_SET: {
    uint32_t result = 0;
    TType elemType;
    uint32_t i, size;
    result += prot.readSetBegin(elemType, size);
    for (i = 0; i < size; i++) {
      result += skip(prot, elemType);
    }
    result += prot.readSetEnd();
    return result;
  }
  case T_LIST: {
    uint32_t result = 0;
    TType elemType;
    uint32_t i, size;
    result += prot.readListBegin(elemType, size);
    for (i = 0; i < size; i++) {
      result += skip(prot, elemType);
    }
    result += prot.readListEnd();
    return result;
  }
  default:
    break;
  }

  throw TProtocolException(TProtocolException::INVALID_DATA,
                           "invalid TType");
}

}}} // apache::thrift::protocol

#endif // #define _THRIFT_PROTOCOL_TPROTOCOL_H_ 1

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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 _THRIFT_PROTOCOL_TVIRTUALPROTOCOL_H_
#define _THRIFT_PROTOCOL_TVIRTUALPROTOCOL_H_ 1

// #include <thrift/protocol/TProtocol.h>

namespace apache {
namespace thrift {
namespace protocol {

using apache::thrift::transport::TTransport;

/**
 * Helper class that provides default implementations of TProtocol methods.
 *
 * This class provides default implementations of the non-virtual TProtocol
 * methods.  It exists primarily so TVirtualProtocol can derive from it.  It
 * prevents TVirtualProtocol methods from causing infinite recursion if the
 * non-virtual methods are not overridden by the TVirtualProtocol subclass.
 *
 * You probably don't want to use this class directly.  Use TVirtualProtocol
 * instead.
 */
class TProtocolDefaults : public TProtocol {
public:
  uint32_t readMessageBegin(std::string& name, TMessageType& messageType, int32_t& seqid) {
    (void)name;
    (void)messageType;
    (void)seqid;
    throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
                             "this protocol does not support reading (yet).");
  }

  uint32_t readMessageEnd() {
    throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
                             "this protocol does not support reading (yet).");
  }

  uint32_t readStructBegin(std::string& name) {
    (void)name;
    throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
                             "this protocol does not support reading (yet).");
  }

  uint32_t readStructEnd() {
    throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
                             "this protocol does not support reading (yet).");
  }

  uint32_t readFieldBegin(std::string& name, TType& fieldType, int16_t& fieldId) {
    (void)name;
    (void)fieldType;
    (void)fieldId;
    throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
                             "this protocol does not support reading (yet).");
  }

  uint32_t readFieldEnd() {
    throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
                             "this protocol does not support reading (yet).");
  }

  uint32_t readMapBegin(TType& keyType, TType& valType, uint32_t& size) {
    (void)keyType;
    (void)valType;
    (void)size;
    throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
                             "this protocol does not support reading (yet).");
  }

  uint32_t readMapEnd() {
    throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
                             "this protocol does not support reading (yet).");
  }

  uint32_t readListBegin(TType& elemType, uint32_t& size) {
    (void)elemType;
    (void)size;
    throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
                             "this protocol does not support reading (yet).");
  }

  uint32_t readListEnd() {
    throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
                             "this protocol does not support reading (yet).");
  }

  uint32_t readSetBegin(TType& elemType, uint32_t& size) {
    (void)elemType;
    (void)size;
    throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
                             "this protocol does not support reading (yet).");
  }

  uint32_t readSetEnd() {
    throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
                             "this protocol does not support reading (yet).");
  }

  uint32_t readBool(bool& value) {
    (void)value;
    throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
                             "this protocol does not support reading (yet).");
  }

  uint32_t readBool(std::vector<bool>::reference value) {
    (void)value;
    throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
                             "this protocol does not support reading (yet).");
  }

  uint32_t readByte(int8_t& byte) {
    (void)byte;
    throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
                             "this protocol does not support reading (yet).");
  }

  uint32_t readI16(int16_t& i16) {
    (void)i16;
    throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
                             "this protocol does not support reading (yet).");
  }

  uint32_t readI32(int32_t& i32) {
    (void)i32;
    throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
                             "this protocol does not support reading (yet).");
  }

  uint32_t readI64(int64_t& i64) {
    (void)i64;
    throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
                             "this protocol does not support reading (yet).");
  }

  uint32_t readDouble(double& dub) {
    (void)dub;
    throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
                             "this protocol does not support reading (yet).");
  }

  uint32_t readString(std::string& str) {
    (void)str;
    throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
                             "this protocol does not support reading (yet).");
  }

  uint32_t readBinary(std::string& str) {
    (void)str;
    throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
                             "this protocol does not support reading (yet).");
  }

  uint32_t writeMessageBegin(const std::string& name,
                             const TMessageType messageType,
                             const int32_t seqid) {
    (void)name;
    (void)messageType;
    (void)seqid;
    throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
                             "this protocol does not support writing (yet).");
  }

  uint32_t writeMessageEnd() {
    throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
                             "this protocol does not support writing (yet).");
  }

  uint32_t writeStructBegin(const char* name) {
    (void)name;
    throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
                             "this protocol does not support writing (yet).");
  }

  uint32_t writeStructEnd() {
    throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
                             "this protocol does not support writing (yet).");
  }

  uint32_t writeFieldBegin(const char* name, const TType fieldType, const int16_t fieldId) {
    (void)name;
    (void)fieldType;
    (void)fieldId;
    throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
                             "this protocol does not support writing (yet).");
  }

  uint32_t writeFieldEnd() {
    throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
                             "this protocol does not support writing (yet).");
  }

  uint32_t writeFieldStop() {
    throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
                             "this protocol does not support writing (yet).");
  }

  uint32_t writeMapBegin(const TType keyType, const TType valType, const uint32_t size) {
    (void)keyType;
    (void)valType;
    (void)size;
    throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
                             "this protocol does not support writing (yet).");
  }

  uint32_t writeMapEnd() {
    throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
                             "this protocol does not support writing (yet).");
  }

  uint32_t writeListBegin(const TType elemType, const uint32_t size) {
    (void)elemType;
    (void)size;
    throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
                             "this protocol does not support writing (yet).");
  }

  uint32_t writeListEnd() {
    throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
                             "this protocol does not support writing (yet).");
  }

  uint32_t writeSetBegin(const TType elemType, const uint32_t size) {
    (void)elemType;
    (void)size;
    throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
                             "this protocol does not support writing (yet).");
  }

  uint32_t writeSetEnd() {
    throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
                             "this protocol does not support writing (yet).");
  }

  uint32_t writeBool(const bool value) {
    (void)value;
    throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
                             "this protocol does not support writing (yet).");
  }

  uint32_t writeByte(const int8_t byte) {
    (void)byte;
    throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
                             "this protocol does not support writing (yet).");
  }

  uint32_t writeI16(const int16_t i16) {
    (void)i16;
    throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
                             "this protocol does not support writing (yet).");
  }

  uint32_t writeI32(const int32_t i32) {
    (void)i32;
    throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
                             "this protocol does not support writing (yet).");
  }

  uint32_t writeI64(const int64_t i64) {
    (void)i64;
    throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
                             "this protocol does not support writing (yet).");
  }

  uint32_t writeDouble(const double dub) {
    (void)dub;
    throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
                             "this protocol does not support writing (yet).");
  }

  uint32_t writeString(const std::string& str) {
    (void)str;
    throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
                             "this protocol does not support writing (yet).");
  }

  uint32_t writeBinary(const std::string& str) {
    (void)str;
    throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
                             "this protocol does not support writing (yet).");
  }

  uint32_t skip(TType type) { return ::apache::thrift::protocol::skip(*this, type); }

protected:
  TProtocolDefaults(std::shared_ptr<TTransport> ptrans) : TProtocol(ptrans) {}
};

/**
 * Concrete TProtocol classes should inherit from TVirtualProtocol
 * so they don't have to manually override virtual methods.
 */
template <class Protocol_, class Super_ = TProtocolDefaults>
class TVirtualProtocol : public Super_ {
public:
  /**
   * Writing functions.
   */

  uint32_t writeMessageBegin_virt(const std::string& name,
                                          const TMessageType messageType,
                                          const int32_t seqid) override {
    return static_cast<Protocol_*>(this)->writeMessageBegin(name, messageType, seqid);
  }

  uint32_t writeMessageEnd_virt() override {
    return static_cast<Protocol_*>(this)->writeMessageEnd();
  }

  uint32_t writeStructBegin_virt(const char* name) override {
    return static_cast<Protocol_*>(this)->writeStructBegin(name);
  }

  uint32_t writeStructEnd_virt() override { return static_cast<Protocol_*>(this)->writeStructEnd(); }

  uint32_t writeFieldBegin_virt(const char* name,
                                        const TType fieldType,
                                        const int16_t fieldId) override {
    return static_cast<Protocol_*>(this)->writeFieldBegin(name, fieldType, fieldId);
  }

  uint32_t writeFieldEnd_virt() override { return static_cast<Protocol_*>(this)->writeFieldEnd(); }

  uint32_t writeFieldStop_virt() override { return static_cast<Protocol_*>(this)->writeFieldStop(); }

  uint32_t writeMapBegin_virt(const TType keyType,
                                      const TType valType,
                                      const uint32_t size) override {
    return static_cast<Protocol_*>(this)->writeMapBegin(keyType, valType, size);
  }

  uint32_t writeMapEnd_virt() override { return static_cast<Protocol_*>(this)->writeMapEnd(); }

  uint32_t writeListBegin_virt(const TType elemType, const uint32_t size) override {
    return static_cast<Protocol_*>(this)->writeListBegin(elemType, size);
  }

  uint32_t writeListEnd_virt() override { return static_cast<Protocol_*>(this)->writeListEnd(); }

  uint32_t writeSetBegin_virt(const TType elemType, const uint32_t size) override {
    return static_cast<Protocol_*>(this)->writeSetBegin(elemType, size);
  }

  uint32_t writeSetEnd_virt() override { return static_cast<Protocol_*>(this)->writeSetEnd(); }

  uint32_t writeBool_virt(const bool value) override {
    return static_cast<Protocol_*>(this)->writeBool(value);
  }

  uint32_t writeByte_virt(const int8_t byte) override {
    return static_cast<Protocol_*>(this)->writeByte(byte);
  }

  uint32_t writeI16_virt(const int16_t i16) override {
    return static_cast<Protocol_*>(this)->writeI16(i16);
  }

  uint32_t writeI32_virt(const int32_t i32) override {
    return static_cast<Protocol_*>(this)->writeI32(i32);
  }

  uint32_t writeI64_virt(const int64_t i64) override {
    return static_cast<Protocol_*>(this)->writeI64(i64);
  }

  uint32_t writeDouble_virt(const double dub) override {
    return static_cast<Protocol_*>(this)->writeDouble(dub);
  }

  uint32_t writeString_virt(const std::string& str) override {
    return static_cast<Protocol_*>(this)->writeString(str);
  }

  uint32_t writeBinary_virt(const std::string& str) override {
    return static_cast<Protocol_*>(this)->writeBinary(str);
  }

  /**
   * Reading functions
   */

  uint32_t readMessageBegin_virt(std::string& name,
                                         TMessageType& messageType,
                                         int32_t& seqid) override {
    return static_cast<Protocol_*>(this)->readMessageBegin(name, messageType, seqid);
  }

  uint32_t readMessageEnd_virt() override { return static_cast<Protocol_*>(this)->readMessageEnd(); }

  uint32_t readStructBegin_virt(std::string& name) override {
    return static_cast<Protocol_*>(this)->readStructBegin(name);
  }

  uint32_t readStructEnd_virt() override { return static_cast<Protocol_*>(this)->readStructEnd(); }

  uint32_t readFieldBegin_virt(std::string& name, TType& fieldType, int16_t& fieldId) override {
    return static_cast<Protocol_*>(this)->readFieldBegin(name, fieldType, fieldId);
  }

  uint32_t readFieldEnd_virt() override { return static_cast<Protocol_*>(this)->readFieldEnd(); }

  uint32_t readMapBegin_virt(TType& keyType, TType& valType, uint32_t& size) override {
    return static_cast<Protocol_*>(this)->readMapBegin(keyType, valType, size);
  }

  uint32_t readMapEnd_virt() override { return static_cast<Protocol_*>(this)->readMapEnd(); }

  uint32_t readListBegin_virt(TType& elemType, uint32_t& size) override {
    return static_cast<Protocol_*>(this)->readListBegin(elemType, size);
  }

  uint32_t readListEnd_virt() override { return static_cast<Protocol_*>(this)->readListEnd(); }

  uint32_t readSetBegin_virt(TType& elemType, uint32_t& size) override {
    return static_cast<Protocol_*>(this)->readSetBegin(elemType, size);
  }

  uint32_t readSetEnd_virt() override { return static_cast<Protocol_*>(this)->readSetEnd(); }

  uint32_t readBool_virt(bool& value) override {
    return static_cast<Protocol_*>(this)->readBool(value);
  }

  uint32_t readBool_virt(std::vector<bool>::reference value) override {
    return static_cast<Protocol_*>(this)->readBool(value);
  }

  uint32_t readByte_virt(int8_t& byte) override {
    return static_cast<Protocol_*>(this)->readByte(byte);
  }

  uint32_t readI16_virt(int16_t& i16) override {
    return static_cast<Protocol_*>(this)->readI16(i16);
  }

  uint32_t readI32_virt(int32_t& i32) override {
    return static_cast<Protocol_*>(this)->readI32(i32);
  }

  uint32_t readI64_virt(int64_t& i64) override {
    return static_cast<Protocol_*>(this)->readI64(i64);
  }

  uint32_t readDouble_virt(double& dub) override {
    return static_cast<Protocol_*>(this)->readDouble(dub);
  }

  uint32_t readString_virt(std::string& str) override {
    return static_cast<Protocol_*>(this)->readString(str);
  }

  uint32_t readBinary_virt(std::string& str) override {
    return static_cast<Protocol_*>(this)->readBinary(str);
  }

  uint32_t skip_virt(TType type) override { return static_cast<Protocol_*>(this)->skip(type); }

  /*
   * Provide a default skip() implementation that uses non-virtual read
   * methods.
   *
   * Note: subclasses that use TVirtualProtocol to derive from another protocol
   * implementation (i.e., not TProtocolDefaults) should beware that this may
   * override any non-default skip() implementation provided by the parent
   * transport class.  They may need to explicitly redefine skip() to call the
   * correct parent implementation, if desired.
   */
  uint32_t skip(TType type) {
    auto* const prot = static_cast<Protocol_*>(this);
    return ::apache::thrift::protocol::skip(*prot, type);
  }

  /*
   * Provide a default readBool() implementation for use with
   * std::vector<bool>, that behaves the same as reading into a normal bool.
   *
   * Subclasses can override this if desired, but there normally shouldn't
   * be a need to.
   */
  uint32_t readBool(std::vector<bool>::reference value) {
    bool b = false;
    uint32_t ret = static_cast<Protocol_*>(this)->readBool(b);
    value = b;
    return ret;
  }
  using Super_::readBool; // so we don't hide readBool(bool&)

protected:
  TVirtualProtocol(std::shared_ptr<TTransport> ptrans) : Super_(ptrans) {}
};
}
}
} // apache::thrift::protocol

#endif // #define _THRIFT_PROTOCOL_TVIRTUALPROTOCOL_H_ 1

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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 _THRIFT_PROTOCOL_TCOMPACTPROTOCOL_H_
#define _THRIFT_PROTOCOL_TCOMPACTPROTOCOL_H_ 1

// #include <thrift/protocol/TVirtualProtocol.h>

#include <stack>
#include <memory>

namespace apache {
namespace thrift {
namespace protocol {

/**
 * C++ Implementation of the Compact Protocol as described in THRIFT-110
 */
template <class Transport_>
class TCompactProtocolT : public TVirtualProtocol<TCompactProtocolT<Transport_> > {
public:
  static const int8_t PROTOCOL_ID = (int8_t)0x82u;
  static const int8_t VERSION_N = 1;
  static const int8_t VERSION_MASK = 0x1f;       // 0001 1111

protected:
  static const int8_t TYPE_MASK = (int8_t)0xE0u; // 1110 0000
  static const int8_t TYPE_BITS = 0x07;          // 0000 0111
  static const int32_t TYPE_SHIFT_AMOUNT = 5;

  Transport_* trans_;

  /**
   * (Writing) If we encounter a boolean field begin, save the TField here
   * so it can have the value incorporated.
   */
  struct {
    const char* name;
    TType fieldType;
    int16_t fieldId;
  } booleanField_;

  /**
   * (Reading) If we read a field header, and it's a boolean field, save
   * the boolean value here so that readBool can use it.
   */
  struct {
    bool hasBoolValue;
    bool boolValue;
  } boolValue_;

  /**
   * Used to keep track of the last field for the current and previous structs,
   * so we can do the delta stuff.
   */

  std::stack<int16_t> lastField_;
  int16_t lastFieldId_;

public:
  TCompactProtocolT(std::shared_ptr<Transport_> trans)
    : TVirtualProtocol<TCompactProtocolT<Transport_> >(trans),
      trans_(trans.get()),
      lastFieldId_(0),
      string_limit_(0),
      string_buf_(nullptr),
      string_buf_size_(0),
      container_limit_(0) {
    booleanField_.name = nullptr;
    boolValue_.hasBoolValue = false;
  }

  TCompactProtocolT(std::shared_ptr<Transport_> trans,
                    int32_t string_limit,
                    int32_t container_limit)
    : TVirtualProtocol<TCompactProtocolT<Transport_> >(trans),
      trans_(trans.get()),
      lastFieldId_(0),
      string_limit_(string_limit),
      string_buf_(nullptr),
      string_buf_size_(0),
      container_limit_(container_limit) {
    booleanField_.name = nullptr;
    boolValue_.hasBoolValue = false;
  }

  ~TCompactProtocolT() override { free(string_buf_); }

  /**
   * Writing functions
   */

  virtual uint32_t writeMessageBegin(const std::string& name,
                                     const TMessageType messageType,
                                     const int32_t seqid);

  uint32_t writeStructBegin(const char* name);

  uint32_t writeStructEnd();

  uint32_t writeFieldBegin(const char* name, const TType fieldType, const int16_t fieldId);

  uint32_t writeFieldStop();

  uint32_t writeListBegin(const TType elemType, const uint32_t size);

  uint32_t writeSetBegin(const TType elemType, const uint32_t size);

  virtual uint32_t writeMapBegin(const TType keyType, const TType valType, const uint32_t size);

  uint32_t writeBool(const bool value);

  uint32_t writeByte(const int8_t byte);

  uint32_t writeI16(const int16_t i16);

  uint32_t writeI32(const int32_t i32);

  uint32_t writeI64(const int64_t i64);

  uint32_t writeDouble(const double dub);

  uint32_t writeString(const std::string& str);

  uint32_t writeBinary(const std::string& str);

  /**
  * These methods are called by structs, but don't actually have any wired
  * output or purpose
  */
  virtual uint32_t writeMessageEnd() { return 0; }
  uint32_t writeMapEnd() { return 0; }
  uint32_t writeListEnd() { return 0; }
  uint32_t writeSetEnd() { return 0; }
  uint32_t writeFieldEnd() { return 0; }

protected:
  int32_t writeFieldBeginInternal(const char* name,
                                  const TType fieldType,
                                  const int16_t fieldId,
                                  int8_t typeOverride);
  uint32_t writeCollectionBegin(const TType elemType, int32_t size);
  uint32_t writeVarint32(uint32_t n);
  uint32_t writeVarint64(uint64_t n);
  uint64_t i64ToZigzag(const int64_t l);
  uint32_t i32ToZigzag(const int32_t n);
  inline int8_t getCompactType(const TType ttype);

public:
  uint32_t readMessageBegin(std::string& name, TMessageType& messageType, int32_t& seqid);

  uint32_t readStructBegin(std::string& name);

  uint32_t readStructEnd();

  uint32_t readFieldBegin(std::string& name, TType& fieldType, int16_t& fieldId);

  uint32_t readMapBegin(TType& keyType, TType& valType, uint32_t& size);

  uint32_t readListBegin(TType& elemType, uint32_t& size);

  uint32_t readSetBegin(TType& elemType, uint32_t& size);

  uint32_t readBool(bool& value);
  // Provide the default readBool() implementation for std::vector<bool>
  using TVirtualProtocol<TCompactProtocolT<Transport_> >::readBool;

  uint32_t readByte(int8_t& byte);

  uint32_t readI16(int16_t& i16);

  uint32_t readI32(int32_t& i32);

  uint32_t readI64(int64_t& i64);

  uint32_t readDouble(double& dub);

  uint32_t readString(std::string& str);

  uint32_t readBinary(std::string& str);

  /*
   *These methods are here for the struct to call, but don't have any wire
   * encoding.
   */
  uint32_t readMessageEnd() { return 0; }
  uint32_t readFieldEnd() { return 0; }
  uint32_t readMapEnd() { return 0; }
  uint32_t readListEnd() { return 0; }
  uint32_t readSetEnd() { return 0; }

protected:
  uint32_t readVarint32(int32_t& i32);
  uint32_t readVarint64(int64_t& i64);
  int32_t zigzagToI32(uint32_t n);
  int64_t zigzagToI64(uint64_t n);
  TType getTType(int8_t type);

  // Buffer for reading strings, save for the lifetime of the protocol to
  // avoid memory churn allocating memory on every string read
  int32_t string_limit_;
  uint8_t* string_buf_;
  int32_t string_buf_size_;
  int32_t container_limit_;
};

typedef TCompactProtocolT<TTransport> TCompactProtocol;

/**
 * Constructs compact protocol handlers
 */
template <class Transport_>
class TCompactProtocolFactoryT : public TProtocolFactory {
public:
  TCompactProtocolFactoryT() : string_limit_(0), container_limit_(0) {}

  TCompactProtocolFactoryT(int32_t string_limit, int32_t container_limit)
    : string_limit_(string_limit), container_limit_(container_limit) {}

  ~TCompactProtocolFactoryT() override = default;

  void setStringSizeLimit(int32_t string_limit) { string_limit_ = string_limit; }

  void setContainerSizeLimit(int32_t container_limit) { container_limit_ = container_limit; }

  std::shared_ptr<TProtocol> getProtocol(std::shared_ptr<TTransport> trans) override {
    std::shared_ptr<Transport_> specific_trans = std::dynamic_pointer_cast<Transport_>(trans);
    TProtocol* prot;
    if (specific_trans) {
      prot = new TCompactProtocolT<Transport_>(specific_trans, string_limit_, container_limit_);
    } else {
      prot = new TCompactProtocol(trans, string_limit_, container_limit_);
    }

    return std::shared_ptr<TProtocol>(prot);
  }

private:
  int32_t string_limit_;
  int32_t container_limit_;
};

typedef TCompactProtocolFactoryT<TTransport> TCompactProtocolFactory;
}
}
} // apache::thrift::protocol

// #include <thrift/protocol/TCompactProtocol.tcc>

#endif

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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 _THRIFT_PROTOCOL_TCOMPACTPROTOCOL_TCC_
#define _THRIFT_PROTOCOL_TCOMPACTPROTOCOL_TCC_ 1

#include <limits>

// #include "thrift/config.h"

/*
 * TCompactProtocol::i*ToZigzag depend on the fact that the right shift
 * operator on a signed integer is an arithmetic (sign-extending) shift.
 * If this is not the case, the current implementation will not work.
 * If anyone encounters this error, we can try to figure out the best
 * way to implement an arithmetic right shift on their platform.
 */
#if !defined(SIGNED_RIGHT_SHIFT_IS) || !defined(ARITHMETIC_RIGHT_SHIFT)
# error "Unable to determine the behavior of a signed right shift"
#endif
#if SIGNED_RIGHT_SHIFT_IS != ARITHMETIC_RIGHT_SHIFT
# error "TCompactProtocol currently only works if a signed right shift is arithmetic"
#endif

#ifdef __GNUC__
#define UNLIKELY(val) (__builtin_expect((val), 0))
#else
#define UNLIKELY(val) (val)
#endif

namespace apache { namespace thrift { namespace protocol {

namespace detail { namespace compact {

enum Types {
  CT_STOP           = 0x00,
  CT_BOOLEAN_TRUE   = 0x01,
  CT_BOOLEAN_FALSE  = 0x02,
  CT_BYTE           = 0x03,
  CT_I16            = 0x04,
  CT_I32            = 0x05,
  CT_I64            = 0x06,
  CT_DOUBLE         = 0x07,
  CT_BINARY         = 0x08,
  CT_LIST           = 0x09,
  CT_SET            = 0x0A,
  CT_MAP            = 0x0B,
  CT_STRUCT         = 0x0C
};

const int8_t TTypeToCType[16] = {
  CT_STOP, // T_STOP
  0, // unused
  CT_BOOLEAN_TRUE, // T_BOOL
  CT_BYTE, // T_BYTE
  CT_DOUBLE, // T_DOUBLE
  0, // unused
  CT_I16, // T_I16
  0, // unused
  CT_I32, // T_I32
  0, // unused
  CT_I64, // T_I64
  CT_BINARY, // T_STRING
  CT_STRUCT, // T_STRUCT
  CT_MAP, // T_MAP
  CT_SET, // T_SET
  CT_LIST, // T_LIST
};

}} // end detail::compact namespace


template <class Transport_>
uint32_t TCompactProtocolT<Transport_>::writeMessageBegin(
    const std::string& name,
    const TMessageType messageType,
    const int32_t seqid) {
  uint32_t wsize = 0;
  wsize += writeByte(PROTOCOL_ID);
  wsize += writeByte((VERSION_N & VERSION_MASK) | (((int32_t)messageType << TYPE_SHIFT_AMOUNT) & TYPE_MASK));
  wsize += writeVarint32(seqid);
  wsize += writeString(name);
  return wsize;
}

/**
 * Write a field header containing the field id and field type. If the
 * difference between the current field id and the last one is small (< 15),
 * then the field id will be encoded in the 4 MSB as a delta. Otherwise, the
 * field id will follow the type header as a zigzag varint.
 */
template <class Transport_>
uint32_t TCompactProtocolT<Transport_>::writeFieldBegin(const char* name,
                                                        const TType fieldType,
                                                        const int16_t fieldId) {
  if (fieldType == T_BOOL) {
    booleanField_.name = name;
    booleanField_.fieldType = fieldType;
    booleanField_.fieldId = fieldId;
  } else {
    return writeFieldBeginInternal(name, fieldType, fieldId, -1);
  }
  return 0;
}

/**
 * Write the STOP symbol so we know there are no more fields in this struct.
 */
template <class Transport_>
uint32_t TCompactProtocolT<Transport_>::writeFieldStop() {
  return writeByte(T_STOP);
}

/**
 * Write a struct begin. This doesn't actually put anything on the wire. We
 * use it as an opportunity to put special placeholder markers on the field
 * stack so we can get the field id deltas correct.
 */
template <class Transport_>
uint32_t TCompactProtocolT<Transport_>::writeStructBegin(const char* name) {
  (void) name;
  lastField_.push(lastFieldId_);
  lastFieldId_ = 0;
  return 0;
}

/**
 * Write a struct end. This doesn't actually put anything on the wire. We use
 * this as an opportunity to pop the last field from the current struct off
 * of the field stack.
 */
template <class Transport_>
uint32_t TCompactProtocolT<Transport_>::writeStructEnd() {
  lastFieldId_ = lastField_.top();
  lastField_.pop();
  return 0;
}

/**
 * Write a List header.
 */
template <class Transport_>
uint32_t TCompactProtocolT<Transport_>::writeListBegin(const TType elemType,
                                                       const uint32_t size) {
  return writeCollectionBegin(elemType, size);
}

/**
 * Write a set header.
 */
template <class Transport_>
uint32_t TCompactProtocolT<Transport_>::writeSetBegin(const TType elemType,
                                                      const uint32_t size) {
  return writeCollectionBegin(elemType, size);
}

/**
 * Write a map header. If the map is empty, omit the key and value type
 * headers, as we don't need any additional information to skip it.
 */
template <class Transport_>
uint32_t TCompactProtocolT<Transport_>::writeMapBegin(const TType keyType,
                                                      const TType valType,
                                                      const uint32_t size) {
  uint32_t wsize = 0;

  if (size == 0) {
    wsize += writeByte(0);
  } else {
    wsize += writeVarint32(size);
    wsize += writeByte(getCompactType(keyType) << 4 | getCompactType(valType));
  }
  return wsize;
}

/**
 * Write a boolean value. Potentially, this could be a boolean field, in
 * which case the field header info isn't written yet. If so, decide what the
 * right type header is for the value and then write the field header.
 * Otherwise, write a single byte.
 */
template <class Transport_>
uint32_t TCompactProtocolT<Transport_>::writeBool(const bool value) {
  uint32_t wsize = 0;

  if (booleanField_.name != NULL) {
    // we haven't written the field header yet
    wsize
      += writeFieldBeginInternal(booleanField_.name,
                                 booleanField_.fieldType,
                                 booleanField_.fieldId,
                                 static_cast<int8_t>(value
                                                     ? detail::compact::CT_BOOLEAN_TRUE
                                                     : detail::compact::CT_BOOLEAN_FALSE));
    booleanField_.name = NULL;
  } else {
    // we're not part of a field, so just write the value
    wsize
      += writeByte(static_cast<int8_t>(value
                                       ? detail::compact::CT_BOOLEAN_TRUE
                                       : detail::compact::CT_BOOLEAN_FALSE));
  }
  return wsize;
}

template <class Transport_>
uint32_t TCompactProtocolT<Transport_>::writeByte(const int8_t byte) {
  trans_->write((uint8_t*)&byte, 1);
  return 1;
}

/**
 * Write an i16 as a zigzag varint.
 */
template <class Transport_>
uint32_t TCompactProtocolT<Transport_>::writeI16(const int16_t i16) {
  return writeVarint32(i32ToZigzag(i16));
}

/**
 * Write an i32 as a zigzag varint.
 */
template <class Transport_>
uint32_t TCompactProtocolT<Transport_>::writeI32(const int32_t i32) {
  return writeVarint32(i32ToZigzag(i32));
}

/**
 * Write an i64 as a zigzag varint.
 */
template <class Transport_>
uint32_t TCompactProtocolT<Transport_>::writeI64(const int64_t i64) {
  return writeVarint64(i64ToZigzag(i64));
}

/**
 * Write a double to the wire as 8 bytes.
 */
template <class Transport_>
uint32_t TCompactProtocolT<Transport_>::writeDouble(const double dub) {
  //BOOST_STATIC_ASSERT(sizeof(double) == sizeof(uint64_t));
  //BOOST_STATIC_ASSERT(std::numeric_limits<double>::is_iec559);

  uint64_t bits = bitwise_cast<uint64_t>(dub);
  bits = THRIFT_htolell(bits);
  trans_->write((uint8_t*)&bits, 8);
  return 8;
}

/**
 * Write a string to the wire with a varint size preceding.
 */
template <class Transport_>
uint32_t TCompactProtocolT<Transport_>::writeString(const std::string& str) {
  return writeBinary(str);
}

template <class Transport_>
uint32_t TCompactProtocolT<Transport_>::writeBinary(const std::string& str) {
  if(str.size() > (std::numeric_limits<uint32_t>::max)())
    throw TProtocolException(TProtocolException::SIZE_LIMIT);
  uint32_t ssize = static_cast<uint32_t>(str.size());
  uint32_t wsize = writeVarint32(ssize) ;
  // checking ssize + wsize > uint_max, but we don't want to overflow while checking for overflows.
  // transforming the check to ssize > uint_max - wsize
  if(ssize > (std::numeric_limits<uint32_t>::max)() - wsize)
    throw TProtocolException(TProtocolException::SIZE_LIMIT);
  wsize += ssize;
  trans_->write((uint8_t*)str.data(), ssize);
  return wsize;
}

//
// Internal Writing methods
//

/**
 * The workhorse of writeFieldBegin. It has the option of doing a
 * 'type override' of the type header. This is used specifically in the
 * boolean field case.
 */
template <class Transport_>
int32_t TCompactProtocolT<Transport_>::writeFieldBeginInternal(
    const char* name,
    const TType fieldType,
    const int16_t fieldId,
    int8_t typeOverride) {
  (void) name;
  uint32_t wsize = 0;

  // if there's a type override, use that.
  int8_t typeToWrite = (typeOverride == -1 ? getCompactType(fieldType) : typeOverride);

  // check if we can use delta encoding for the field id
  if (fieldId > lastFieldId_ && fieldId - lastFieldId_ <= 15) {
    // write them together
    wsize += writeByte(static_cast<int8_t>((fieldId - lastFieldId_)
                                           << 4 | typeToWrite));
  } else {
    // write them separate
    wsize += writeByte(typeToWrite);
    wsize += writeI16(fieldId);
  }

  lastFieldId_ = fieldId;
  return wsize;
}

/**
 * Abstract method for writing the start of lists and sets. List and sets on
 * the wire differ only by the type indicator.
 */
template <class Transport_>
uint32_t TCompactProtocolT<Transport_>::writeCollectionBegin(const TType elemType,
                                                             int32_t size) {
  uint32_t wsize = 0;
  if (size <= 14) {
    wsize += writeByte(static_cast<int8_t>(size
                                           << 4 | getCompactType(elemType)));
  } else {
    wsize += writeByte(0xf0 | getCompactType(elemType));
    wsize += writeVarint32(size);
  }
  return wsize;
}

/**
 * Write an i32 as a varint. Results in 1-5 bytes on the wire.
 */
template <class Transport_>
uint32_t TCompactProtocolT<Transport_>::writeVarint32(uint32_t n) {
  uint8_t buf[5];
  uint32_t wsize = 0;

  while (true) {
    if ((n & ~0x7F) == 0) {
      buf[wsize++] = (int8_t)n;
      break;
    } else {
      buf[wsize++] = (int8_t)((n & 0x7F) | 0x80);
      n >>= 7;
    }
  }
  trans_->write(buf, wsize);
  return wsize;
}

/**
 * Write an i64 as a varint. Results in 1-10 bytes on the wire.
 */
template <class Transport_>
uint32_t TCompactProtocolT<Transport_>::writeVarint64(uint64_t n) {
  uint8_t buf[10];
  uint32_t wsize = 0;

  while (true) {
    if ((n & ~0x7FL) == 0) {
      buf[wsize++] = (int8_t)n;
      break;
    } else {
      buf[wsize++] = (int8_t)((n & 0x7F) | 0x80);
      n >>= 7;
    }
  }
  trans_->write(buf, wsize);
  return wsize;
}

/**
 * Convert l into a zigzag long. This allows negative numbers to be
 * represented compactly as a varint.
 */
template <class Transport_>
uint64_t TCompactProtocolT<Transport_>::i64ToZigzag(const int64_t l) {
  return (static_cast<uint64_t>(l) << 1) ^ (l >> 63);
}

/**
 * Convert n into a zigzag int. This allows negative numbers to be
 * represented compactly as a varint.
 */
template <class Transport_>
uint32_t TCompactProtocolT<Transport_>::i32ToZigzag(const int32_t n) {
  return (static_cast<uint32_t>(n) << 1) ^ (n >> 31);
}

/**
 * Given a TType value, find the appropriate detail::compact::Types value
 */
template <class Transport_>
int8_t TCompactProtocolT<Transport_>::getCompactType(const TType ttype) {
  return detail::compact::TTypeToCType[ttype];
}

//
// Reading Methods
//

/**
 * Read a message header.
 */
template <class Transport_>
uint32_t TCompactProtocolT<Transport_>::readMessageBegin(
    std::string& name,
    TMessageType& messageType,
    int32_t& seqid) {
  uint32_t rsize = 0;
  int8_t protocolId;
  int8_t versionAndType;
  int8_t version;

  rsize += readByte(protocolId);
  if (protocolId != PROTOCOL_ID) {
    throw TProtocolException(TProtocolException::BAD_VERSION, "Bad protocol identifier");
  }

  rsize += readByte(versionAndType);
  version = (int8_t)(versionAndType & VERSION_MASK);
  if (version != VERSION_N) {
    throw TProtocolException(TProtocolException::BAD_VERSION, "Bad protocol version");
  }

  messageType = (TMessageType)((versionAndType >> TYPE_SHIFT_AMOUNT) & TYPE_BITS);
  rsize += readVarint32(seqid);
  rsize += readString(name);

  return rsize;
}

/**
 * Read a struct begin. There's nothing on the wire for this, but it is our
 * opportunity to push a new struct begin marker on the field stack.
 */
template <class Transport_>
uint32_t TCompactProtocolT<Transport_>::readStructBegin(std::string& name) {
  name = "";
  lastField_.push(lastFieldId_);
  lastFieldId_ = 0;
  return 0;
}

/**
 * Doesn't actually consume any wire data, just removes the last field for
 * this struct from the field stack.
 */
template <class Transport_>
uint32_t TCompactProtocolT<Transport_>::readStructEnd() {
  lastFieldId_ = lastField_.top();
  lastField_.pop();
  return 0;
}

/**
 * Read a field header off the wire.
 */
template <class Transport_>
uint32_t TCompactProtocolT<Transport_>::readFieldBegin(std::string& name,
                                                       TType& fieldType,
                                                       int16_t& fieldId) {
  (void) name;
  uint32_t rsize = 0;
  int8_t byte;
  int8_t type;

  rsize += readByte(byte);
  type = (byte & 0x0f);

  // if it's a stop, then we can return immediately, as the struct is over.
  if (type == T_STOP) {
    fieldType = T_STOP;
    fieldId = 0;
    return rsize;
  }

  // mask off the 4 MSB of the type header. it could contain a field id delta.
  int16_t modifier = (int16_t)(((uint8_t)byte & 0xf0) >> 4);
  if (modifier == 0) {
    // not a delta, look ahead for the zigzag varint field id.
    rsize += readI16(fieldId);
  } else {
    fieldId = (int16_t)(lastFieldId_ + modifier);
  }
  fieldType = getTType(type);

  // if this happens to be a boolean field, the value is encoded in the type
  if (type == detail::compact::CT_BOOLEAN_TRUE ||
      type == detail::compact::CT_BOOLEAN_FALSE) {
    // save the boolean value in a special instance variable.
    boolValue_.hasBoolValue = true;
    boolValue_.boolValue =
      (type == detail::compact::CT_BOOLEAN_TRUE ? true : false);
  }

  // push the new field onto the field stack so we can keep the deltas going.
  lastFieldId_ = fieldId;
  return rsize;
}

/**
 * Read a map header off the wire. If the size is zero, skip reading the key
 * and value type. This means that 0-length maps will yield TMaps without the
 * "correct" types.
 */
template <class Transport_>
uint32_t TCompactProtocolT<Transport_>::readMapBegin(TType& keyType,
                                                     TType& valType,
                                                     uint32_t& size) {
  uint32_t rsize = 0;
  int8_t kvType = 0;
  int32_t msize = 0;

  rsize += readVarint32(msize);
  if (msize != 0)
    rsize += readByte(kvType);

  if (msize < 0) {
    throw TProtocolException(TProtocolException::NEGATIVE_SIZE);
  } else if (container_limit_ && msize > container_limit_) {
    throw TProtocolException(TProtocolException::SIZE_LIMIT);
  }

  keyType = getTType((int8_t)((uint8_t)kvType >> 4));
  valType = getTType((int8_t)((uint8_t)kvType & 0xf));
  size = (uint32_t)msize;

  return rsize;
}

/**
 * Read a list header off the wire. If the list size is 0-14, the size will
 * be packed into the element type header. If it's a longer list, the 4 MSB
 * of the element type header will be 0xF, and a varint will follow with the
 * true size.
 */
template <class Transport_>
uint32_t TCompactProtocolT<Transport_>::readListBegin(TType& elemType,
                                                      uint32_t& size) {
  int8_t size_and_type;
  uint32_t rsize = 0;
  int32_t lsize;

  rsize += readByte(size_and_type);

  lsize = ((uint8_t)size_and_type >> 4) & 0x0f;
  if (lsize == 15) {
    rsize += readVarint32(lsize);
  }

  if (lsize < 0) {
    throw TProtocolException(TProtocolException::NEGATIVE_SIZE);
  } else if (container_limit_ && lsize > container_limit_) {
    throw TProtocolException(TProtocolException::SIZE_LIMIT);
  }

  elemType = getTType((int8_t)(size_and_type & 0x0f));
  size = (uint32_t)lsize;

  return rsize;
}

/**
 * Read a set header off the wire. If the set size is 0-14, the size will
 * be packed into the element type header. If it's a longer set, the 4 MSB
 * of the element type header will be 0xF, and a varint will follow with the
 * true size.
 */
template <class Transport_>
uint32_t TCompactProtocolT<Transport_>::readSetBegin(TType& elemType,
                                                     uint32_t& size) {
  return readListBegin(elemType, size);
}

/**
 * Read a boolean off the wire. If this is a boolean field, the value should
 * already have been read during readFieldBegin, so we'll just consume the
 * pre-stored value. Otherwise, read a byte.
 */
template <class Transport_>
uint32_t TCompactProtocolT<Transport_>::readBool(bool& value) {
  if (boolValue_.hasBoolValue == true) {
    value = boolValue_.boolValue;
    boolValue_.hasBoolValue = false;
    return 0;
  } else {
    int8_t val;
    readByte(val);
    value = (val == detail::compact::CT_BOOLEAN_TRUE);
    return 1;
  }
}

/**
 * Read a single byte off the wire. Nothing interesting here.
 */
template <class Transport_>
uint32_t TCompactProtocolT<Transport_>::readByte(int8_t& byte) {
  uint8_t b[1];
  trans_->readAll(b, 1);
  byte = *(int8_t*)b;
  return 1;
}

/**
 * Read an i16 from the wire as a zigzag varint.
 */
template <class Transport_>
uint32_t TCompactProtocolT<Transport_>::readI16(int16_t& i16) {
  int32_t value;
  uint32_t rsize = readVarint32(value);
  i16 = (int16_t)zigzagToI32(value);
  return rsize;
}

/**
 * Read an i32 from the wire as a zigzag varint.
 */
template <class Transport_>
uint32_t TCompactProtocolT<Transport_>::readI32(int32_t& i32) {
  int32_t value;
  uint32_t rsize = readVarint32(value);
  i32 = zigzagToI32(value);
  return rsize;
}

/**
 * Read an i64 from the wire as a zigzag varint.
 */
template <class Transport_>
uint32_t TCompactProtocolT<Transport_>::readI64(int64_t& i64) {
  int64_t value;
  uint32_t rsize = readVarint64(value);
  i64 = zigzagToI64(value);
  return rsize;
}

/**
 * No magic here - just read a double off the wire.
 */
template <class Transport_>
uint32_t TCompactProtocolT<Transport_>::readDouble(double& dub) {
  //BOOST_STATIC_ASSERT(sizeof(double) == sizeof(uint64_t));
  //BOOST_STATIC_ASSERT(std::numeric_limits<double>::is_iec559);

  union {
    uint64_t bits;
    uint8_t b[8];
  } u;
  trans_->readAll(u.b, 8);
  u.bits = THRIFT_letohll(u.bits);
  dub = bitwise_cast<double>(u.bits);
  return 8;
}

template <class Transport_>
uint32_t TCompactProtocolT<Transport_>::readString(std::string& str) {
  return readBinary(str);
}

/**
 * Read a byte[] from the wire.
 */
template <class Transport_>
uint32_t TCompactProtocolT<Transport_>::readBinary(std::string& str) {
  int32_t rsize = 0;
  int32_t size;

  rsize += readVarint32(size);
  // Catch empty string case
  if (size == 0) {
    str = "";
    return rsize;
  }

  // Catch error cases
  if (size < 0) {
    throw TProtocolException(TProtocolException::NEGATIVE_SIZE);
  }
  if (string_limit_ > 0 && size > string_limit_) {
    throw TProtocolException(TProtocolException::SIZE_LIMIT);
  }

  // Use the heap here to prevent stack overflow for v. large strings
  if (size > string_buf_size_ || string_buf_ == NULL) {
    void* new_string_buf = std::realloc(string_buf_, (uint32_t)size);
    if (new_string_buf == NULL) {
      throw std::bad_alloc();
    }
    string_buf_ = (uint8_t*)new_string_buf;
    string_buf_size_ = size;
  }
  trans_->readAll(string_buf_, size);
  str.assign((char*)string_buf_, size);

  return rsize + (uint32_t)size;
}

/**
 * Read an i32 from the wire as a varint. The MSB of each byte is set
 * if there is another byte to follow. This can read up to 5 bytes.
 */
template <class Transport_>
uint32_t TCompactProtocolT<Transport_>::readVarint32(int32_t& i32) {
  int64_t val;
  uint32_t rsize = readVarint64(val);
  i32 = (int32_t)val;
  return rsize;
}

/**
 * Read an i64 from the wire as a proper varint. The MSB of each byte is set
 * if there is another byte to follow. This can read up to 10 bytes.
 */
template <class Transport_>
uint32_t TCompactProtocolT<Transport_>::readVarint64(int64_t& i64) {
  uint32_t rsize = 0;
  uint64_t val = 0;
  int shift = 0;
  uint8_t buf[10];  // 64 bits / (7 bits/byte) = 10 bytes.
  uint32_t buf_size = sizeof(buf);
  const uint8_t* borrowed = trans_->borrow(buf, &buf_size);

  // Fast path.
  if (borrowed != NULL) {
    while (true) {
      uint8_t byte = borrowed[rsize];
      rsize++;
      val |= (uint64_t)(byte & 0x7f) << shift;
      shift += 7;
      if (!(byte & 0x80)) {
        i64 = val;
        trans_->consume(rsize);
        return rsize;
      }
      // Have to check for invalid data so we don't crash.
      if (UNLIKELY(rsize == sizeof(buf))) {
        throw TProtocolException(TProtocolException::INVALID_DATA, "Variable-length int over 10 bytes.");
      }
    }
  }

  // Slow path.
  else {
    while (true) {
      uint8_t byte;
      rsize += trans_->readAll(&byte, 1);
      val |= (uint64_t)(byte & 0x7f) << shift;
      shift += 7;
      if (!(byte & 0x80)) {
        i64 = val;
        return rsize;
      }
      // Might as well check for invalid data on the slow path too.
      if (UNLIKELY(rsize >= sizeof(buf))) {
        throw TProtocolException(TProtocolException::INVALID_DATA, "Variable-length int over 10 bytes.");
      }
    }
  }
}

/**
 * Convert from zigzag int to int.
 */
template <class Transport_>
int32_t TCompactProtocolT<Transport_>::zigzagToI32(uint32_t n) {
  return (n >> 1) ^ static_cast<uint32_t>(-static_cast<int32_t>(n & 1));
}

/**
 * Convert from zigzag long to long.
 */
template <class Transport_>
int64_t TCompactProtocolT<Transport_>::zigzagToI64(uint64_t n) {
  return (n >> 1) ^ static_cast<uint64_t>(-static_cast<int64_t>(n & 1));
}

template <class Transport_>
TType TCompactProtocolT<Transport_>::getTType(int8_t type) {
  switch (type) {
    case T_STOP:
      return T_STOP;
    case detail::compact::CT_BOOLEAN_FALSE:
    case detail::compact::CT_BOOLEAN_TRUE:
      return T_BOOL;
    case detail::compact::CT_BYTE:
      return T_BYTE;
    case detail::compact::CT_I16:
      return T_I16;
    case detail::compact::CT_I32:
      return T_I32;
    case detail::compact::CT_I64:
      return T_I64;
    case detail::compact::CT_DOUBLE:
      return T_DOUBLE;
    case detail::compact::CT_BINARY:
      return T_STRING;
    case detail::compact::CT_LIST:
      return T_LIST;
    case detail::compact::CT_SET:
      return T_SET;
    case detail::compact::CT_MAP:
      return T_MAP;
    case detail::compact::CT_STRUCT:
      return T_STRUCT;
    default:
      throw TException(std::string("don't know what type: ") + (char)type);
  }
}

}}} // apache::thrift::protocol

#endif // _THRIFT_PROTOCOL_TCOMPACTPROTOCOL_TCC_

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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 _THRIFT_TRANSPORT_TVIRTUALTRANSPORT_H_
#define _THRIFT_TRANSPORT_TVIRTUALTRANSPORT_H_ 1

// #include <thrift/transport/TTransport.h>

namespace apache {
namespace thrift {
namespace transport {

/**
 * Helper class that provides default implementations of TTransport methods.
 *
 * This class provides default implementations of read(), readAll(), write(),
 * borrow() and consume().
 *
 * In the TTransport base class, each of these methods simply invokes its
 * virtual counterpart.  This class overrides them to always perform the
 * default behavior, without a virtual function call.
 *
 * The primary purpose of this class is to serve as a base class for
 * TVirtualTransport, and prevent infinite recursion if one of its subclasses
 * does not override the TTransport implementation of these methods.  (Since
 * TVirtualTransport::read_virt() calls read(), and TTransport::read() calls
 * read_virt().)
 */
class TTransportDefaults : public TTransport {
public:
  /*
   * TTransport *_virt() methods provide reasonable default implementations.
   * Invoke them non-virtually.
   */
  uint32_t read(uint8_t* buf, uint32_t len) { return this->TTransport::read_virt(buf, len); }
  uint32_t readAll(uint8_t* buf, uint32_t len) { return this->TTransport::readAll_virt(buf, len); }
  void write(const uint8_t* buf, uint32_t len) { this->TTransport::write_virt(buf, len); }
  const uint8_t* borrow(uint8_t* buf, uint32_t* len) {
    return this->TTransport::borrow_virt(buf, len);
  }
  void consume(uint32_t len) { this->TTransport::consume_virt(len); }

protected:
  TTransportDefaults() = default;
};

/**
 * Helper class to provide polymorphism for subclasses of TTransport.
 *
 * This class implements *_virt() methods of TTransport, to call the
 * non-virtual versions of these functions in the proper subclass.
 *
 * To define your own transport class using TVirtualTransport:
 * 1) Derive your subclass from TVirtualTransport<your class>
 *    e.g:  class MyTransport : public TVirtualTransport<MyTransport> {
 * 2) Provide your own implementations of read(), readAll(), etc.
 *    These methods should be non-virtual.
 *
 * Transport implementations that need to use virtual inheritance when
 * inheriting from TTransport cannot use TVirtualTransport.
 *
 * @author Chad Walters <chad@powerset.com>
 */
template <class Transport_, class Super_ = TTransportDefaults>
class TVirtualTransport : public Super_ {
public:
  /*
   * Implementations of the *_virt() functions, to call the subclass's
   * non-virtual implementation function.
   */
  uint32_t read_virt(uint8_t* buf, uint32_t len) override {
    return static_cast<Transport_*>(this)->read(buf, len);
  }

  uint32_t readAll_virt(uint8_t* buf, uint32_t len) override {
    return static_cast<Transport_*>(this)->readAll(buf, len);
  }

  void write_virt(const uint8_t* buf, uint32_t len) override {
    static_cast<Transport_*>(this)->write(buf, len);
  }

  const uint8_t* borrow_virt(uint8_t* buf, uint32_t* len) override {
    return static_cast<Transport_*>(this)->borrow(buf, len);
  }

  void consume_virt(uint32_t len) override { static_cast<Transport_*>(this)->consume(len); }

  /*
   * Provide a default readAll() implementation that invokes
   * read() non-virtually.
   *
   * Note: subclasses that use TVirtualTransport to derive from another
   * transport implementation (i.e., not TTransportDefaults) should beware that
   * this may override any non-default readAll() implementation provided by
   * the parent transport class.  They may need to redefine readAll() to call
   * the correct parent implementation, if desired.
   */
  uint32_t readAll(uint8_t* buf, uint32_t len) {
    auto* trans = static_cast<Transport_*>(this);
    return ::apache::thrift::transport::readAll(*trans, buf, len);
  }

protected:
  TVirtualTransport() = default;

  /*
   * Templatized constructors, to allow arguments to be passed to the Super_
   * constructor.  Currently we only support 0, 1, or 2 arguments, but
   * additional versions can be added as needed.
   */
  template <typename Arg_>
  TVirtualTransport(Arg_ const& arg)
    : Super_(arg) {}

  template <typename Arg1_, typename Arg2_>
  TVirtualTransport(Arg1_ const& a1, Arg2_ const& a2)
    : Super_(a1, a2) {}
};
}
}
} // apache::thrift::transport

#endif // #ifndef _THRIFT_TRANSPORT_TVIRTUALTRANSPORT_H_

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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 _THRIFT_TRANSPORT_TBUFFERTRANSPORTS_H_
#define _THRIFT_TRANSPORT_TBUFFERTRANSPORTS_H_ 1

#include <cstdlib>
#include <cstddef>
#include <cstring>
#include <limits>
//#include <boost/scoped_array.hpp> // FUCK OFF

// #include <thrift/transport/TTransport.h>
// #include <thrift/transport/TVirtualTransport.h>

#ifdef __GNUC__
#define TDB_LIKELY(val) (__builtin_expect((val), 1))
#define TDB_UNLIKELY(val) (__builtin_expect((val), 0))
#else
#define TDB_LIKELY(val) (val)
#define TDB_UNLIKELY(val) (val)
#endif

namespace apache {
namespace thrift {
namespace transport {

/**
 * Base class for all transports that use read/write buffers for performance.
 *
 * TBufferBase is designed to implement the fast-path "memcpy" style
 * operations that work in the common case.  It does so with small and
 * (eventually) nonvirtual, inlinable methods.  TBufferBase is an abstract
 * class.  Subclasses are expected to define the "slow path" operations
 * that have to be done when the buffers are full or empty.
 *
 */
class TBufferBase : public TVirtualTransport<TBufferBase> {

public:
  /**
   * Fast-path read.
   *
   * When we have enough data buffered to fulfill the read, we can satisfy it
   * with a single memcpy, then adjust our internal pointers.  If the buffer
   * is empty, we call out to our slow path, implemented by a subclass.
   * This method is meant to eventually be nonvirtual and inlinable.
   */
  uint32_t read(uint8_t* buf, uint32_t len) {
    uint8_t* new_rBase = rBase_ + len;
    if (TDB_LIKELY(new_rBase <= rBound_)) {
      std::memcpy(buf, rBase_, len);
      rBase_ = new_rBase;
      return len;
    }
    return readSlow(buf, len);
  }

  /**
   * Shortcutted version of readAll.
   */
  uint32_t readAll(uint8_t* buf, uint32_t len) {
    uint8_t* new_rBase = rBase_ + len;
    if (TDB_LIKELY(new_rBase <= rBound_)) {
      std::memcpy(buf, rBase_, len);
      rBase_ = new_rBase;
      return len;
    }
    return apache::thrift::transport::readAll(*this, buf, len);
  }

  /**
   * Fast-path write.
   *
   * When we have enough empty space in our buffer to accommodate the write, we
   * can satisfy it with a single memcpy, then adjust our internal pointers.
   * If the buffer is full, we call out to our slow path, implemented by a
   * subclass.  This method is meant to eventually be nonvirtual and
   * inlinable.
   */
  void write(const uint8_t* buf, uint32_t len) {
    uint8_t* new_wBase = wBase_ + len;
    if (TDB_LIKELY(new_wBase <= wBound_)) {
      std::memcpy(wBase_, buf, len);
      wBase_ = new_wBase;
      return;
    }
    writeSlow(buf, len);
  }

  /**
   * Fast-path borrow.  A lot like the fast-path read.
   */
  const uint8_t* borrow(uint8_t* buf, uint32_t* len) {
    if (TDB_LIKELY(static_cast<ptrdiff_t>(*len) <= rBound_ - rBase_)) {
      // With strict aliasing, writing to len shouldn't force us to
      // refetch rBase_ from memory.  TODO(dreiss): Verify this.
      *len = static_cast<uint32_t>(rBound_ - rBase_);
      return rBase_;
    }
    return borrowSlow(buf, len);
  }

  /**
   * Consume doesn't require a slow path.
   */
  void consume(uint32_t len) {
    if (TDB_LIKELY(static_cast<ptrdiff_t>(len) <= rBound_ - rBase_)) {
      rBase_ += len;
    } else {
      throw TTransportException(TTransportException::BAD_ARGS, "consume did not follow a borrow.");
    }
  }

protected:
  /// Slow path read.
  virtual uint32_t readSlow(uint8_t* buf, uint32_t len) = 0;

  /// Slow path write.
  virtual void writeSlow(const uint8_t* buf, uint32_t len) = 0;

  /**
   * Slow path borrow.
   *
   * POSTCONDITION: return == NULL || rBound_ - rBase_ >= *len
   */
  virtual const uint8_t* borrowSlow(uint8_t* buf, uint32_t* len) = 0;

  /**
   * Trivial constructor.
   *
   * Initialize pointers safely.  Constructing is not a very
   * performance-sensitive operation, so it is okay to just leave it to
   * the concrete class to set up pointers correctly.
   */
  TBufferBase() : rBase_(nullptr), rBound_(nullptr), wBase_(nullptr), wBound_(nullptr) {}

  /// Convenience mutator for setting the read buffer.
  void setReadBuffer(uint8_t* buf, uint32_t len) {
    rBase_ = buf;
    rBound_ = buf + len;
  }

  /// Convenience mutator for setting the write buffer.
  void setWriteBuffer(uint8_t* buf, uint32_t len) {
    wBase_ = buf;
    wBound_ = buf + len;
  }

  ~TBufferBase() override = default;

  /// Reads begin here.
  uint8_t* rBase_;
  /// Reads may extend to just before here.
  uint8_t* rBound_;

  /// Writes begin here.
  uint8_t* wBase_;
  /// Writes may extend to just before here.
  uint8_t* wBound_;
};


/**
 * A memory buffer is a tranpsort that simply reads from and writes to an
 * in memory buffer. Anytime you call write on it, the data is simply placed
 * into a buffer, and anytime you call read, data is read from that buffer.
 *
 * The buffers are allocated using C constructs malloc,realloc, and the size
 * doubles as necessary.  We've considered using scoped
 *
 */
class TMemoryBuffer : public TVirtualTransport<TMemoryBuffer, TBufferBase> {
private:
  // Common initialization done by all constructors.
  void initCommon(uint8_t* buf, uint32_t size, bool owner, uint32_t wPos) {

    maxBufferSize_ = (std::numeric_limits<uint32_t>::max)();

    if (buf == nullptr && size != 0) {
      assert(owner);
      buf = (uint8_t*)std::malloc(size);
      if (buf == nullptr) {
	throw std::bad_alloc();
      }
    }

    buffer_ = buf;
    bufferSize_ = size;

    rBase_ = buffer_;
    rBound_ = buffer_ + wPos;
    // TODO(dreiss): Investigate NULL-ing this if !owner.
    wBase_ = buffer_ + wPos;
    wBound_ = buffer_ + bufferSize_;

    owner_ = owner;

    // rBound_ is really an artifact.  In principle, it should always be
    // equal to wBase_.  We update it in a few places (computeRead, etc.).
  }

public:
  static const uint32_t defaultSize = 1024;

  /**
   * This enum specifies how a TMemoryBuffer should treat
   * memory passed to it via constructors or resetBuffer.
   *
   * OBSERVE:
   *   TMemoryBuffer will simply store a pointer to the memory.
   *   It is the callers responsibility to ensure that the pointer
   *   remains valid for the lifetime of the TMemoryBuffer,
   *   and that it is properly cleaned up.
   *   Note that no data can be written to observed buffers.
   *
   * COPY:
   *   TMemoryBuffer will make an internal copy of the buffer.
   *   The caller has no responsibilities.
   *
   * TAKE_OWNERSHIP:
   *   TMemoryBuffer will become the "owner" of the buffer,
   *   and will be responsible for freeing it.
   *   The membory must have been allocated with malloc.
   */
  enum MemoryPolicy { OBSERVE = 1, COPY = 2, TAKE_OWNERSHIP = 3 };

  /**
   * Construct a TMemoryBuffer with a default-sized buffer,
   * owned by the TMemoryBuffer object.
   */
  TMemoryBuffer() { initCommon(nullptr, defaultSize, true, 0); }

  /**
   * Construct a TMemoryBuffer with a buffer of a specified size,
   * owned by the TMemoryBuffer object.
   *
   * @param sz  The initial size of the buffer.
   */
  TMemoryBuffer(uint32_t sz) { initCommon(nullptr, sz, true, 0); }

  /**
   * Construct a TMemoryBuffer with buf as its initial contents.
   *
   * @param buf    The initial contents of the buffer.
   *               Note that, while buf is a non-const pointer,
   *               TMemoryBuffer will not write to it if policy == OBSERVE,
   *               so it is safe to const_cast<uint8_t*>(whatever).
   * @param sz     The size of @c buf.
   * @param policy See @link MemoryPolicy @endlink .
   */
  TMemoryBuffer(uint8_t* buf, uint32_t sz, MemoryPolicy policy = OBSERVE) {
    if (buf == nullptr && sz != 0) {
      throw TTransportException(TTransportException::BAD_ARGS,
                                "TMemoryBuffer given null buffer with non-zero size.");
    }

    switch (policy) {
    case OBSERVE:
    case TAKE_OWNERSHIP:
      initCommon(buf, sz, policy == TAKE_OWNERSHIP, sz);
      break;
    case COPY:
      initCommon(nullptr, sz, true, 0);
      this->write(buf, sz);
      break;
    default:
      throw TTransportException(TTransportException::BAD_ARGS,
                                "Invalid MemoryPolicy for TMemoryBuffer");
    }
  }

  ~TMemoryBuffer() override {
    if (owner_) {
      std::free(buffer_);
    }
  }

  bool isOpen() const override { return true; }

  bool peek() override { return (rBase_ < wBase_); }

  void open() override {}

  void close() override {}

  // TODO(dreiss): Make bufPtr const.
  void getBuffer(uint8_t** bufPtr, uint32_t* sz) {
    *bufPtr = rBase_;
    *sz = static_cast<uint32_t>(wBase_ - rBase_);
  }

  std::string getBufferAsString() {
    if (buffer_ == nullptr) {
      return "";
    }
    uint8_t* buf;
    uint32_t sz;
    getBuffer(&buf, &sz);
    return std::string((char*)buf, (std::string::size_type)sz);
  }

  void appendBufferToString(std::string& str) {
    if (buffer_ == nullptr) {
      return;
    }
    uint8_t* buf;
    uint32_t sz;
    getBuffer(&buf, &sz);
    str.append((char*)buf, sz);
  }

  void resetBuffer() {
    rBase_ = buffer_;
    rBound_ = buffer_;
    wBase_ = buffer_;
    // It isn't safe to write into a buffer we don't own.
    if (!owner_) {
      wBound_ = wBase_;
      bufferSize_ = 0;
    }
  }

  /// See constructor documentation.
  void resetBuffer(uint8_t* buf, uint32_t sz, MemoryPolicy policy = OBSERVE) {
    // Use a variant of the copy-and-swap trick for assignment operators.
    // This is sub-optimal in terms of performance for two reasons:
    //   1/ The constructing and swapping of the (small) values
    //      in the temporary object takes some time, and is not necessary.
    //   2/ If policy == COPY, we allocate the new buffer before
    //      freeing the old one, precluding the possibility of
    //      reusing that memory.
    // I doubt that either of these problems could be optimized away,
    // but the second is probably no a common case, and the first is minor.
    // I don't expect resetBuffer to be a common operation, so I'm willing to
    // bite the performance bullet to make the method this simple.

    // Construct the new buffer.
    TMemoryBuffer new_buffer(buf, sz, policy);
    // Move it into ourself.
    this->swap(new_buffer);
    // Our old self gets destroyed.
  }

  /// See constructor documentation.
  void resetBuffer(uint32_t sz) {
    // Construct the new buffer.
    TMemoryBuffer new_buffer(sz);
    // Move it into ourself.
    this->swap(new_buffer);
    // Our old self gets destroyed.
  }

  std::string readAsString(uint32_t len) {
    std::string str;
    (void)readAppendToString(str, len);
    return str;
  }

  uint32_t readAppendToString(std::string& str, uint32_t len);

  // return number of bytes read
  uint32_t readEnd() override {
    // This cast should be safe, because buffer_'s size is a uint32_t
    auto bytes = static_cast<uint32_t>(rBase_ - buffer_);
    if (rBase_ == wBase_) {
      resetBuffer();
    }
    return bytes;
  }

  // Return number of bytes written
  uint32_t writeEnd() override {
    // This cast should be safe, because buffer_'s size is a uint32_t
    return static_cast<uint32_t>(wBase_ - buffer_);
  }

  uint32_t available_read() const {
    // Remember, wBase_ is the real rBound_.
    return static_cast<uint32_t>(wBase_ - rBase_);
  }

  uint32_t available_write() const { return static_cast<uint32_t>(wBound_ - wBase_); }

  // Returns a pointer to where the client can write data to append to
  // the TMemoryBuffer, and ensures the buffer is big enough to accommodate a
  // write of the provided length.  The returned pointer is very convenient for
  // passing to read(), recv(), or similar. You must call wroteBytes() as soon
  // as data is written or the buffer will not be aware that data has changed.
  uint8_t* getWritePtr(uint32_t len) {
    ensureCanWrite(len);
    return wBase_;
  }

  // Informs the buffer that the client has written 'len' bytes into storage
  // that had been provided by getWritePtr().
  void wroteBytes(uint32_t len);

  /*
   * TVirtualTransport provides a default implementation of readAll().
   * We want to use the TBufferBase version instead.
   */
  uint32_t readAll(uint8_t* buf, uint32_t len) { return TBufferBase::readAll(buf, len); }

  //! \brief Get the current buffer size
  //! \returns the current buffer size
  uint32_t getBufferSize() const {
    return bufferSize_;
  }

  //! \brief Get the current maximum buffer size
  //! \returns the current maximum buffer size
  uint32_t getMaxBufferSize() const {
    return maxBufferSize_;
  }

  //! \brief Change the maximum buffer size
  //! \param[in]  maxSize  the new maximum buffer size allowed to grow to
  //! \throws  TTransportException(BAD_ARGS) if maxSize is less than the current buffer size
  void setMaxBufferSize(uint32_t maxSize) {
    if (maxSize < bufferSize_) {
      throw TTransportException(TTransportException::BAD_ARGS,
                                "Maximum buffer size would be less than current buffer size");
    }
    maxBufferSize_ = maxSize;
  }

protected:
  void swap(TMemoryBuffer& that) {
    using std::swap;
    swap(buffer_, that.buffer_);
    swap(bufferSize_, that.bufferSize_);

    swap(rBase_, that.rBase_);
    swap(rBound_, that.rBound_);
    swap(wBase_, that.wBase_);
    swap(wBound_, that.wBound_);

    swap(owner_, that.owner_);
  }

  // Make sure there's at least 'len' bytes available for writing.
  void ensureCanWrite(uint32_t len);

  // Compute the position and available data for reading.
  void computeRead(uint32_t len, uint8_t** out_start, uint32_t* out_give);

  uint32_t readSlow(uint8_t* buf, uint32_t len) override;

  void writeSlow(const uint8_t* buf, uint32_t len) override;

  const uint8_t* borrowSlow(uint8_t* buf, uint32_t* len) override;

  // Data buffer
  uint8_t* buffer_;

  // Allocated buffer size
  uint32_t bufferSize_;

  // Maximum allowed size
  uint32_t maxBufferSize_;

  // Is this object the owner of the buffer?
  bool owner_;

  // Don't forget to update constrctors, initCommon, and swap if
  // you add new members.
};
}
}
} // apache::thrift::transport

#endif // #ifndef _THRIFT_TRANSPORT_TBUFFERTRANSPORTS_H_

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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 _THRIFT_TBASE_H_
#define _THRIFT_TBASE_H_ 1

// #include <thrift/Thrift.h>
// #include <thrift/protocol/TProtocol.h>

namespace apache {
namespace thrift {

class TBase {
public:
  virtual ~TBase() = default;
  virtual uint32_t read(protocol::TProtocol* iprot) = 0;
  virtual uint32_t write(protocol::TProtocol* oprot) const = 0;
};
}
} // apache::thrift

#endif // #ifndef _THRIFT_TBASE_H_

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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 _THRIFT_TOSTRING_H_
#define _THRIFT_TOSTRING_H_ 1

#include <cmath>
#include <limits>
#include <map>
#include <set>
#include <sstream>
#include <string>
#include <vector>

namespace apache {
namespace thrift {

template <typename T>
std::string to_string(const T& t) {
  std::ostringstream o;
  o << t;
  return o.str();
}

// TODO: replace the computations below with std::numeric_limits::max_digits10 once C++11
// is enabled.
inline std::string to_string(const float& t) {
  std::ostringstream o;
  o.precision(static_cast<std::streamsize>(std::ceil(static_cast<double>(std::numeric_limits<float>::digits * std::log10(2.0f) + 1))));
  o << t;
  return o.str();
}

inline std::string to_string(const double& t) {
  std::ostringstream o;
  o.precision(static_cast<std::streamsize>(std::ceil(static_cast<double>(std::numeric_limits<double>::digits * std::log10(2.0f) + 1))));
  o << t;
  return o.str();
}

inline std::string to_string(const long double& t) {
  std::ostringstream o;
  o.precision(static_cast<std::streamsize>(std::ceil(static_cast<double>(std::numeric_limits<long double>::digits * std::log10(2.0f) + 1))));
  o << t;
  return o.str();
}

template <typename K, typename V>
std::string to_string(const std::map<K, V>& m);

template <typename T>
std::string to_string(const std::set<T>& s);

template <typename T>
std::string to_string(const std::vector<T>& t);

template <typename K, typename V>
std::string to_string(const typename std::pair<K, V>& v) {
  std::ostringstream o;
  o << to_string(v.first) << ": " << to_string(v.second);
  return o.str();
}

template <typename T>
std::string to_string(const T& beg, const T& end) {
  std::ostringstream o;
  for (T it = beg; it != end; ++it) {
    if (it != beg)
      o << ", ";
    o << to_string(*it);
  }
  return o.str();
}

template <typename T>
std::string to_string(const std::vector<T>& t) {
  std::ostringstream o;
  o << "[" << to_string(t.begin(), t.end()) << "]";
  return o.str();
}

template <typename K, typename V>
std::string to_string(const std::map<K, V>& m) {
  std::ostringstream o;
  o << "{" << to_string(m.begin(), m.end()) << "}";
  return o.str();
}

template <typename T>
std::string to_string(const std::set<T>& s) {
  std::ostringstream o;
  o << "{" << to_string(s.begin(), s.end()) << "}";
  return o.str();
}
}
} // apache::thrift

#endif // _THRIFT_TOSTRING_H_

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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.
 */

// #include <thrift/protocol/TProtocol.h>

namespace apache {
namespace thrift {
namespace protocol {

TProtocol::~TProtocol() = default;
uint32_t TProtocol::skip_virt(TType type) {
  return ::apache::thrift::protocol::skip(*this, type);
}

TProtocolFactory::~TProtocolFactory() = default;

}}} // apache::thrift::protocol

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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.
 */

// #include <thrift/transport/TTransportException.h>
#include <cstring>

// #include <thrift/thrift-config.h>

using std::string;

namespace apache {
namespace thrift {
namespace transport {

const char* TTransportException::what() const noexcept {
  if (message_.empty()) {
    switch (type_) {
    case UNKNOWN:
      return "TTransportException: Unknown transport exception";
    case NOT_OPEN:
      return "TTransportException: Transport not open";
    case TIMED_OUT:
      return "TTransportException: Timed out";
    case END_OF_FILE:
      return "TTransportException: End of file";
    case INTERRUPTED:
      return "TTransportException: Interrupted";
    case BAD_ARGS:
      return "TTransportException: Invalid arguments";
    case CORRUPTED_DATA:
      return "TTransportException: Corrupted Data";
    case INTERNAL_ERROR:
      return "TTransportException: Internal error";
    default:
      return "TTransportException: (Invalid exception type)";
    }
  } else {
    return message_.c_str();
  }
}
}
}
} // apache::thrift::transport

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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.
 */

#include <cassert>
#include <algorithm>
// #include <thrift/thrift_export.h>

// #include <thrift/transport/TBufferTransports.h>

using std::string;

namespace apache {
namespace thrift {
namespace transport {




void TMemoryBuffer::computeRead(uint32_t len, uint8_t** out_start, uint32_t* out_give) {
  // Correct rBound_ so we can use the fast path in the future.
  rBound_ = wBase_;

  // Decide how much to give.
  uint32_t give = (std::min)(len, available_read());

  *out_start = rBase_;
  *out_give = give;

  // Preincrement rBase_ so the caller doesn't have to.
  rBase_ += give;
}

uint32_t TMemoryBuffer::readSlow(uint8_t* buf, uint32_t len) {
  uint8_t* start;
  uint32_t give;
  computeRead(len, &start, &give);

  // Copy into the provided buffer.
  memcpy(buf, start, give);

  return give;
}

uint32_t TMemoryBuffer::readAppendToString(std::string& str, uint32_t len) {
  // Don't get some stupid assertion failure.
  if (buffer_ == nullptr) {
    return 0;
  }

  uint8_t* start;
  uint32_t give;
  computeRead(len, &start, &give);

  // Append to the provided string.
  str.append((char*)start, give);

  return give;
}

void TMemoryBuffer::ensureCanWrite(uint32_t len) {
  // Check available space
  uint32_t avail = available_write();
  if (len <= avail) {
    return;
  }

  if (!owner_) {
    throw TTransportException("Insufficient space in external MemoryBuffer");
  }

  // Grow the buffer as necessary.
  uint64_t new_size = bufferSize_;
  while (len > avail) {
    new_size = new_size > 0 ? new_size * 2 : 1;
    if (new_size > maxBufferSize_) {
      throw TTransportException(TTransportException::BAD_ARGS,
                                "Internal buffer size overflow");
    }
    avail = available_write() + (static_cast<uint32_t>(new_size) - bufferSize_);
  }

  // Allocate into a new pointer so we don't bork ours if it fails.
  auto* new_buffer = static_cast<uint8_t*>(std::realloc(buffer_, new_size));
  if (new_buffer == nullptr) {
    throw std::bad_alloc();
  }

  rBase_ = new_buffer + (rBase_ - buffer_);
  rBound_ = new_buffer + (rBound_ - buffer_);
  wBase_ = new_buffer + (wBase_ - buffer_);
  wBound_ = new_buffer + new_size;
  buffer_ = new_buffer;
  bufferSize_ = static_cast<uint32_t>(new_size);
}

void TMemoryBuffer::writeSlow(const uint8_t* buf, uint32_t len) {
  ensureCanWrite(len);

  // Copy into the buffer and increment wBase_.
  memcpy(wBase_, buf, len);
  wBase_ += len;
}

void TMemoryBuffer::wroteBytes(uint32_t len) {
  uint32_t avail = available_write();
  if (len > avail) {
    throw TTransportException("Client wrote more bytes than size of buffer.");
  }
  wBase_ += len;
}

const uint8_t* TMemoryBuffer::borrowSlow(uint8_t* buf, uint32_t* len) {
  (void)buf;
  rBound_ = wBase_;
  if (available_read() >= *len) {
    *len = available_read();
    return rBase_;
  }
  return nullptr;
}
}
}
} // apache::thrift::transport

// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you 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.


#ifdef _WIN32

// Windows defines min and max macros that mess up std::min/max
#ifndef NOMINMAX
#define NOMINMAX
#endif

#define WIN32_LEAN_AND_MEAN

// Set Windows 7 as a conservative minimum for Apache Arrow
#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x601
#undef _WIN32_WINNT
#endif
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x601
#endif

#include <winsock2.h>
#include <windows.h>

#undef OPTIONAL // WTF?!
#undef Realloc
#undef min
#undef max

#endif  // _WIN32

/**
 * Autogenerated by Thrift Compiler (0.11.0)
 *
 * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
 *  @generated
 */
#ifndef parquet_TYPES_H
#define parquet_TYPES_H

#include <iosfwd>

// #include <thrift/Thrift.h>
// #include <thrift/TApplicationException.h>
// #include <thrift/TBase.h>
// #include <thrift/protocol/TProtocol.h>
// #include <thrift/transport/TTransport.h>

// #include <thrift/stdcxx.h>

// #include "windows_compatibility.h"

namespace parquet { namespace format {

struct Type {
  enum type {
    BOOLEAN = 0,
    INT32 = 1,
    INT64 = 2,
    INT96 = 3,
    FLOAT = 4,
    DOUBLE = 5,
    BYTE_ARRAY = 6,
    FIXED_LEN_BYTE_ARRAY = 7
  };
};

extern const std::map<int, const char*> _Type_VALUES_TO_NAMES;

std::ostream& operator<<(std::ostream& out, const Type::type& val);

struct ConvertedType {
  enum type {
    UTF8 = 0,
    MAP = 1,
    MAP_KEY_VALUE = 2,
    LIST = 3,
    ENUM = 4,
    DECIMAL = 5,
    DATE = 6,
    TIME_MILLIS = 7,
    TIME_MICROS = 8,
    TIMESTAMP_MILLIS = 9,
    TIMESTAMP_MICROS = 10,
    UINT_8 = 11,
    UINT_16 = 12,
    UINT_32 = 13,
    UINT_64 = 14,
    INT_8 = 15,
    INT_16 = 16,
    INT_32 = 17,
    INT_64 = 18,
    JSON = 19,
    BSON = 20,
    INTERVAL = 21
  };
};

extern const std::map<int, const char*> _ConvertedType_VALUES_TO_NAMES;

std::ostream& operator<<(std::ostream& out, const ConvertedType::type& val);

struct FieldRepetitionType {
  enum type {
    REQUIRED = 0,
    OPTIONAL = 1,
    REPEATED = 2
  };
};

extern const std::map<int, const char*> _FieldRepetitionType_VALUES_TO_NAMES;

std::ostream& operator<<(std::ostream& out, const FieldRepetitionType::type& val);

struct Encoding {
  enum type {
    PLAIN = 0,
    PLAIN_DICTIONARY = 2,
    RLE = 3,
    BIT_PACKED = 4,
    DELTA_BINARY_PACKED = 5,
    DELTA_LENGTH_BYTE_ARRAY = 6,
    DELTA_BYTE_ARRAY = 7,
    RLE_DICTIONARY = 8
  };
};

extern const std::map<int, const char*> _Encoding_VALUES_TO_NAMES;

std::ostream& operator<<(std::ostream& out, const Encoding::type& val);

struct CompressionCodec {
  enum type {
    UNCOMPRESSED = 0,
    SNAPPY = 1,
    GZIP = 2,
    LZO = 3,
    BROTLI = 4,
    LZ4 = 5,
    ZSTD = 6
  };
};

extern const std::map<int, const char*> _CompressionCodec_VALUES_TO_NAMES;

std::ostream& operator<<(std::ostream& out, const CompressionCodec::type& val);

struct PageType {
  enum type {
    DATA_PAGE = 0,
    INDEX_PAGE = 1,
    DICTIONARY_PAGE = 2,
    DATA_PAGE_V2 = 3
  };
};

extern const std::map<int, const char*> _PageType_VALUES_TO_NAMES;

std::ostream& operator<<(std::ostream& out, const PageType::type& val);

struct BoundaryOrder {
  enum type {
    UNORDERED = 0,
    ASCENDING = 1,
    DESCENDING = 2
  };
};

extern const std::map<int, const char*> _BoundaryOrder_VALUES_TO_NAMES;

std::ostream& operator<<(std::ostream& out, const BoundaryOrder::type& val);

class Statistics;

class StringType;

class UUIDType;

class MapType;

class ListType;

class EnumType;

class DateType;

class NullType;

class DecimalType;

class MilliSeconds;

class MicroSeconds;

class NanoSeconds;

class TimeUnit;

class TimestampType;

class TimeType;

class IntType;

class JsonType;

class BsonType;

class LogicalType;

class SchemaElement;

class DataPageHeader;

class IndexPageHeader;

class DictionaryPageHeader;

class DataPageHeaderV2;

class PageHeader;

class KeyValue;

class SortingColumn;

class PageEncodingStats;

class ColumnMetaData;

class EncryptionWithFooterKey;

class EncryptionWithColumnKey;

class ColumnCryptoMetaData;

class ColumnChunk;

class RowGroup;

class TypeDefinedOrder;

class ColumnOrder;

class PageLocation;

class OffsetIndex;

class ColumnIndex;

class AesGcmV1;

class AesGcmCtrV1;

class EncryptionAlgorithm;

class FileMetaData;

class FileCryptoMetaData;

typedef struct _Statistics__isset {
  _Statistics__isset() : max(false), min(false), null_count(false), distinct_count(false), max_value(false), min_value(false) {}
  bool max :1;
  bool min :1;
  bool null_count :1;
  bool distinct_count :1;
  bool max_value :1;
  bool min_value :1;
} _Statistics__isset;

class Statistics : public virtual ::apache::thrift::TBase {
 public:

  Statistics(const Statistics&);
  Statistics& operator=(const Statistics&);
  Statistics() : max(), min(), null_count(0), distinct_count(0), max_value(), min_value() {
  }

  virtual ~Statistics() throw();
  std::string max;
  std::string min;
  int64_t null_count;
  int64_t distinct_count;
  std::string max_value;
  std::string min_value;

  _Statistics__isset __isset;

  void __set_max(const std::string& val);

  void __set_min(const std::string& val);

  void __set_null_count(const int64_t val);

  void __set_distinct_count(const int64_t val);

  void __set_max_value(const std::string& val);

  void __set_min_value(const std::string& val);

  bool operator == (const Statistics & rhs) const
  {
    if (__isset.max != rhs.__isset.max)
      return false;
    else if (__isset.max && !(max == rhs.max))
      return false;
    if (__isset.min != rhs.__isset.min)
      return false;
    else if (__isset.min && !(min == rhs.min))
      return false;
    if (__isset.null_count != rhs.__isset.null_count)
      return false;
    else if (__isset.null_count && !(null_count == rhs.null_count))
      return false;
    if (__isset.distinct_count != rhs.__isset.distinct_count)
      return false;
    else if (__isset.distinct_count && !(distinct_count == rhs.distinct_count))
      return false;
    if (__isset.max_value != rhs.__isset.max_value)
      return false;
    else if (__isset.max_value && !(max_value == rhs.max_value))
      return false;
    if (__isset.min_value != rhs.__isset.min_value)
      return false;
    else if (__isset.min_value && !(min_value == rhs.min_value))
      return false;
    return true;
  }
  bool operator != (const Statistics &rhs) const {
    return !(*this == rhs);
  }

  bool operator < (const Statistics & ) const;

  uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
  uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;

  virtual void printTo(std::ostream& out) const;
};

void swap(Statistics &a, Statistics &b);

std::ostream& operator<<(std::ostream& out, const Statistics& obj);


class StringType : public virtual ::apache::thrift::TBase {
 public:

  StringType(const StringType&);
  StringType& operator=(const StringType&);
  StringType() {
  }

  virtual ~StringType() throw();

  bool operator == (const StringType & /* rhs */) const
  {
    return true;
  }
  bool operator != (const StringType &rhs) const {
    return !(*this == rhs);
  }

  bool operator < (const StringType & ) const;

  uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
  uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;

  virtual void printTo(std::ostream& out) const;
};

void swap(StringType &a, StringType &b);

std::ostream& operator<<(std::ostream& out, const StringType& obj);


class UUIDType : public virtual ::apache::thrift::TBase {
 public:

  UUIDType(const UUIDType&);
  UUIDType& operator=(const UUIDType&);
  UUIDType() {
  }

  virtual ~UUIDType() throw();

  bool operator == (const UUIDType & /* rhs */) const
  {
    return true;
  }
  bool operator != (const UUIDType &rhs) const {
    return !(*this == rhs);
  }

  bool operator < (const UUIDType & ) const;

  uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
  uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;

  virtual void printTo(std::ostream& out) const;
};

void swap(UUIDType &a, UUIDType &b);

std::ostream& operator<<(std::ostream& out, const UUIDType& obj);


class MapType : public virtual ::apache::thrift::TBase {
 public:

  MapType(const MapType&);
  MapType& operator=(const MapType&);
  MapType() {
  }

  virtual ~MapType() throw();

  bool operator == (const MapType & /* rhs */) const
  {
    return true;
  }
  bool operator != (const MapType &rhs) const {
    return !(*this == rhs);
  }

  bool operator < (const MapType & ) const;

  uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
  uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;

  virtual void printTo(std::ostream& out) const;
};

void swap(MapType &a, MapType &b);

std::ostream& operator<<(std::ostream& out, const MapType& obj);


class ListType : public virtual ::apache::thrift::TBase {
 public:

  ListType(const ListType&);
  ListType& operator=(const ListType&);
  ListType() {
  }

  virtual ~ListType() throw();

  bool operator == (const ListType & /* rhs */) const
  {
    return true;
  }
  bool operator != (const ListType &rhs) const {
    return !(*this == rhs);
  }

  bool operator < (const ListType & ) const;

  uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
  uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;

  virtual void printTo(std::ostream& out) const;
};

void swap(ListType &a, ListType &b);

std::ostream& operator<<(std::ostream& out, const ListType& obj);


class EnumType : public virtual ::apache::thrift::TBase {
 public:

  EnumType(const EnumType&);
  EnumType& operator=(const EnumType&);
  EnumType() {
  }

  virtual ~EnumType() throw();

  bool operator == (const EnumType & /* rhs */) const
  {
    return true;
  }
  bool operator != (const EnumType &rhs) const {
    return !(*this == rhs);
  }

  bool operator < (const EnumType & ) const;

  uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
  uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;

  virtual void printTo(std::ostream& out) const;
};

void swap(EnumType &a, EnumType &b);

std::ostream& operator<<(std::ostream& out, const EnumType& obj);


class DateType : public virtual ::apache::thrift::TBase {
 public:

  DateType(const DateType&);
  DateType& operator=(const DateType&);
  DateType() {
  }

  virtual ~DateType() throw();

  bool operator == (const DateType & /* rhs */) const
  {
    return true;
  }
  bool operator != (const DateType &rhs) const {
    return !(*this == rhs);
  }

  bool operator < (const DateType & ) const;

  uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
  uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;

  virtual void printTo(std::ostream& out) const;
};

void swap(DateType &a, DateType &b);

std::ostream& operator<<(std::ostream& out, const DateType& obj);


class NullType : public virtual ::apache::thrift::TBase {
 public:

  NullType(const NullType&);
  NullType& operator=(const NullType&);
  NullType() {
  }

  virtual ~NullType() throw();

  bool operator == (const NullType & /* rhs */) const
  {
    return true;
  }
  bool operator != (const NullType &rhs) const {
    return !(*this == rhs);
  }

  bool operator < (const NullType & ) const;

  uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
  uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;

  virtual void printTo(std::ostream& out) const;
};

void swap(NullType &a, NullType &b);

std::ostream& operator<<(std::ostream& out, const NullType& obj);


class DecimalType : public virtual ::apache::thrift::TBase {
 public:

  DecimalType(const DecimalType&);
  DecimalType& operator=(const DecimalType&);
  DecimalType() : scale(0), precision(0) {
  }

  virtual ~DecimalType() throw();
  int32_t scale;
  int32_t precision;

  void __set_scale(const int32_t val);

  void __set_precision(const int32_t val);

  bool operator == (const DecimalType & rhs) const
  {
    if (!(scale == rhs.scale))
      return false;
    if (!(precision == rhs.precision))
      return false;
    return true;
  }
  bool operator != (const DecimalType &rhs) const {
    return !(*this == rhs);
  }

  bool operator < (const DecimalType & ) const;

  uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
  uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;

  virtual void printTo(std::ostream& out) const;
};

void swap(DecimalType &a, DecimalType &b);

std::ostream& operator<<(std::ostream& out, const DecimalType& obj);


class MilliSeconds : public virtual ::apache::thrift::TBase {
 public:

  MilliSeconds(const MilliSeconds&);
  MilliSeconds& operator=(const MilliSeconds&);
  MilliSeconds() {
  }

  virtual ~MilliSeconds() throw();

  bool operator == (const MilliSeconds & /* rhs */) const
  {
    return true;
  }
  bool operator != (const MilliSeconds &rhs) const {
    return !(*this == rhs);
  }

  bool operator < (const MilliSeconds & ) const;

  uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
  uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;

  virtual void printTo(std::ostream& out) const;
};

void swap(MilliSeconds &a, MilliSeconds &b);

std::ostream& operator<<(std::ostream& out, const MilliSeconds& obj);


class MicroSeconds : public virtual ::apache::thrift::TBase {
 public:

  MicroSeconds(const MicroSeconds&);
  MicroSeconds& operator=(const MicroSeconds&);
  MicroSeconds() {
  }

  virtual ~MicroSeconds() throw();

  bool operator == (const MicroSeconds & /* rhs */) const
  {
    return true;
  }
  bool operator != (const MicroSeconds &rhs) const {
    return !(*this == rhs);
  }

  bool operator < (const MicroSeconds & ) const;

  uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
  uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;

  virtual void printTo(std::ostream& out) const;
};

void swap(MicroSeconds &a, MicroSeconds &b);

std::ostream& operator<<(std::ostream& out, const MicroSeconds& obj);


class NanoSeconds : public virtual ::apache::thrift::TBase {
 public:

  NanoSeconds(const NanoSeconds&);
  NanoSeconds& operator=(const NanoSeconds&);
  NanoSeconds() {
  }

  virtual ~NanoSeconds() throw();

  bool operator == (const NanoSeconds & /* rhs */) const
  {
    return true;
  }
  bool operator != (const NanoSeconds &rhs) const {
    return !(*this == rhs);
  }

  bool operator < (const NanoSeconds & ) const;

  uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
  uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;

  virtual void printTo(std::ostream& out) const;
};

void swap(NanoSeconds &a, NanoSeconds &b);

std::ostream& operator<<(std::ostream& out, const NanoSeconds& obj);

typedef struct _TimeUnit__isset {
  _TimeUnit__isset() : MILLIS(false), MICROS(false), NANOS(false) {}
  bool MILLIS :1;
  bool MICROS :1;
  bool NANOS :1;
} _TimeUnit__isset;

class TimeUnit : public virtual ::apache::thrift::TBase {
 public:

  TimeUnit(const TimeUnit&);
  TimeUnit& operator=(const TimeUnit&);
  TimeUnit() {
  }

  virtual ~TimeUnit() throw();
  MilliSeconds MILLIS;
  MicroSeconds MICROS;
  NanoSeconds NANOS;

  _TimeUnit__isset __isset;

  void __set_MILLIS(const MilliSeconds& val);

  void __set_MICROS(const MicroSeconds& val);

  void __set_NANOS(const NanoSeconds& val);

  bool operator == (const TimeUnit & rhs) const
  {
    if (__isset.MILLIS != rhs.__isset.MILLIS)
      return false;
    else if (__isset.MILLIS && !(MILLIS == rhs.MILLIS))
      return false;
    if (__isset.MICROS != rhs.__isset.MICROS)
      return false;
    else if (__isset.MICROS && !(MICROS == rhs.MICROS))
      return false;
    if (__isset.NANOS != rhs.__isset.NANOS)
      return false;
    else if (__isset.NANOS && !(NANOS == rhs.NANOS))
      return false;
    return true;
  }
  bool operator != (const TimeUnit &rhs) const {
    return !(*this == rhs);
  }

  bool operator < (const TimeUnit & ) const;

  uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
  uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;

  virtual void printTo(std::ostream& out) const;
};

void swap(TimeUnit &a, TimeUnit &b);

std::ostream& operator<<(std::ostream& out, const TimeUnit& obj);


class TimestampType : public virtual ::apache::thrift::TBase {
 public:

  TimestampType(const TimestampType&);
  TimestampType& operator=(const TimestampType&);
  TimestampType() : isAdjustedToUTC(0) {
  }

  virtual ~TimestampType() throw();
  bool isAdjustedToUTC;
  TimeUnit unit;

  void __set_isAdjustedToUTC(const bool val);

  void __set_unit(const TimeUnit& val);

  bool operator == (const TimestampType & rhs) const
  {
    if (!(isAdjustedToUTC == rhs.isAdjustedToUTC))
      return false;
    if (!(unit == rhs.unit))
      return false;
    return true;
  }
  bool operator != (const TimestampType &rhs) const {
    return !(*this == rhs);
  }

  bool operator < (const TimestampType & ) const;

  uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
  uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;

  virtual void printTo(std::ostream& out) const;
};

void swap(TimestampType &a, TimestampType &b);

std::ostream& operator<<(std::ostream& out, const TimestampType& obj);


class TimeType : public virtual ::apache::thrift::TBase {
 public:

  TimeType(const TimeType&);
  TimeType& operator=(const TimeType&);
  TimeType() : isAdjustedToUTC(0) {
  }

  virtual ~TimeType() throw();
  bool isAdjustedToUTC;
  TimeUnit unit;

  void __set_isAdjustedToUTC(const bool val);

  void __set_unit(const TimeUnit& val);

  bool operator == (const TimeType & rhs) const
  {
    if (!(isAdjustedToUTC == rhs.isAdjustedToUTC))
      return false;
    if (!(unit == rhs.unit))
      return false;
    return true;
  }
  bool operator != (const TimeType &rhs) const {
    return !(*this == rhs);
  }

  bool operator < (const TimeType & ) const;

  uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
  uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;

  virtual void printTo(std::ostream& out) const;
};

void swap(TimeType &a, TimeType &b);

std::ostream& operator<<(std::ostream& out, const TimeType& obj);


class IntType : public virtual ::apache::thrift::TBase {
 public:

  IntType(const IntType&);
  IntType& operator=(const IntType&);
  IntType() : bitWidth(0), isSigned(0) {
  }

  virtual ~IntType() throw();
  int8_t bitWidth;
  bool isSigned;

  void __set_bitWidth(const int8_t val);

  void __set_isSigned(const bool val);

  bool operator == (const IntType & rhs) const
  {
    if (!(bitWidth == rhs.bitWidth))
      return false;
    if (!(isSigned == rhs.isSigned))
      return false;
    return true;
  }
  bool operator != (const IntType &rhs) const {
    return !(*this == rhs);
  }

  bool operator < (const IntType & ) const;

  uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
  uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;

  virtual void printTo(std::ostream& out) const;
};

void swap(IntType &a, IntType &b);

std::ostream& operator<<(std::ostream& out, const IntType& obj);


class JsonType : public virtual ::apache::thrift::TBase {
 public:

  JsonType(const JsonType&);
  JsonType& operator=(const JsonType&);
  JsonType() {
  }

  virtual ~JsonType() throw();

  bool operator == (const JsonType & /* rhs */) const
  {
    return true;
  }
  bool operator != (const JsonType &rhs) const {
    return !(*this == rhs);
  }

  bool operator < (const JsonType & ) const;

  uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
  uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;

  virtual void printTo(std::ostream& out) const;
};

void swap(JsonType &a, JsonType &b);

std::ostream& operator<<(std::ostream& out, const JsonType& obj);


class BsonType : public virtual ::apache::thrift::TBase {
 public:

  BsonType(const BsonType&);
  BsonType& operator=(const BsonType&);
  BsonType() {
  }

  virtual ~BsonType() throw();

  bool operator == (const BsonType & /* rhs */) const
  {
    return true;
  }
  bool operator != (const BsonType &rhs) const {
    return !(*this == rhs);
  }

  bool operator < (const BsonType & ) const;

  uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
  uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;

  virtual void printTo(std::ostream& out) const;
};

void swap(BsonType &a, BsonType &b);

std::ostream& operator<<(std::ostream& out, const BsonType& obj);

typedef struct _LogicalType__isset {
  _LogicalType__isset() : STRING(false), MAP(false), LIST(false), ENUM(false), DECIMAL(false), DATE(false), TIME(false), TIMESTAMP(false), INTEGER(false), UNKNOWN(false), JSON(false), BSON(false), UUID(false) {}
  bool STRING :1;
  bool MAP :1;
  bool LIST :1;
  bool ENUM :1;
  bool DECIMAL :1;
  bool DATE :1;
  bool TIME :1;
  bool TIMESTAMP :1;
  bool INTEGER :1;
  bool UNKNOWN :1;
  bool JSON :1;
  bool BSON :1;
  bool UUID :1;
} _LogicalType__isset;

class LogicalType : public virtual ::apache::thrift::TBase {
 public:

  LogicalType(const LogicalType&);
  LogicalType& operator=(const LogicalType&);
  LogicalType() {
  }

  virtual ~LogicalType() throw();
  StringType STRING;
  MapType MAP;
  ListType LIST;
  EnumType ENUM;
  DecimalType DECIMAL;
  DateType DATE;
  TimeType TIME;
  TimestampType TIMESTAMP;
  IntType INTEGER;
  NullType UNKNOWN;
  JsonType JSON;
  BsonType BSON;
  UUIDType UUID;

  _LogicalType__isset __isset;

  void __set_STRING(const StringType& val);

  void __set_MAP(const MapType& val);

  void __set_LIST(const ListType& val);

  void __set_ENUM(const EnumType& val);

  void __set_DECIMAL(const DecimalType& val);

  void __set_DATE(const DateType& val);

  void __set_TIME(const TimeType& val);

  void __set_TIMESTAMP(const TimestampType& val);

  void __set_INTEGER(const IntType& val);

  void __set_UNKNOWN(const NullType& val);

  void __set_JSON(const JsonType& val);

  void __set_BSON(const BsonType& val);

  void __set_UUID(const UUIDType& val);

  bool operator == (const LogicalType & rhs) const
  {
    if (__isset.STRING != rhs.__isset.STRING)
      return false;
    else if (__isset.STRING && !(STRING == rhs.STRING))
      return false;
    if (__isset.MAP != rhs.__isset.MAP)
      return false;
    else if (__isset.MAP && !(MAP == rhs.MAP))
      return false;
    if (__isset.LIST != rhs.__isset.LIST)
      return false;
    else if (__isset.LIST && !(LIST == rhs.LIST))
      return false;
    if (__isset.ENUM != rhs.__isset.ENUM)
      return false;
    else if (__isset.ENUM && !(ENUM == rhs.ENUM))
      return false;
    if (__isset.DECIMAL != rhs.__isset.DECIMAL)
      return false;
    else if (__isset.DECIMAL && !(DECIMAL == rhs.DECIMAL))
      return false;
    if (__isset.DATE != rhs.__isset.DATE)
      return false;
    else if (__isset.DATE && !(DATE == rhs.DATE))
      return false;
    if (__isset.TIME != rhs.__isset.TIME)
      return false;
    else if (__isset.TIME && !(TIME == rhs.TIME))
      return false;
    if (__isset.TIMESTAMP != rhs.__isset.TIMESTAMP)
      return false;
    else if (__isset.TIMESTAMP && !(TIMESTAMP == rhs.TIMESTAMP))
      return false;
    if (__isset.INTEGER != rhs.__isset.INTEGER)
      return false;
    else if (__isset.INTEGER && !(INTEGER == rhs.INTEGER))
      return false;
    if (__isset.UNKNOWN != rhs.__isset.UNKNOWN)
      return false;
    else if (__isset.UNKNOWN && !(UNKNOWN == rhs.UNKNOWN))
      return false;
    if (__isset.JSON != rhs.__isset.JSON)
      return false;
    else if (__isset.JSON && !(JSON == rhs.JSON))
      return false;
    if (__isset.BSON != rhs.__isset.BSON)
      return false;
    else if (__isset.BSON && !(BSON == rhs.BSON))
      return false;
    if (__isset.UUID != rhs.__isset.UUID)
      return false;
    else if (__isset.UUID && !(UUID == rhs.UUID))
      return false;
    return true;
  }
  bool operator != (const LogicalType &rhs) const {
    return !(*this == rhs);
  }

  bool operator < (const LogicalType & ) const;

  uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
  uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;

  virtual void printTo(std::ostream& out) const;
};

void swap(LogicalType &a, LogicalType &b);

std::ostream& operator<<(std::ostream& out, const LogicalType& obj);

typedef struct _SchemaElement__isset {
  _SchemaElement__isset() : type(false), type_length(false), repetition_type(false), num_children(false), converted_type(false), scale(false), precision(false), field_id(false), logicalType(false) {}
  bool type :1;
  bool type_length :1;
  bool repetition_type :1;
  bool num_children :1;
  bool converted_type :1;
  bool scale :1;
  bool precision :1;
  bool field_id :1;
  bool logicalType :1;
} _SchemaElement__isset;

class SchemaElement : public virtual ::apache::thrift::TBase {
 public:

  SchemaElement(const SchemaElement&);
  SchemaElement& operator=(const SchemaElement&);
  SchemaElement() : type((Type::type)0), type_length(0), repetition_type((FieldRepetitionType::type)0), name(), num_children(0), converted_type((ConvertedType::type)0), scale(0), precision(0), field_id(0) {
  }

  virtual ~SchemaElement() throw();
  Type::type type;
  int32_t type_length;
  FieldRepetitionType::type repetition_type;
  std::string name;
  int32_t num_children;
  ConvertedType::type converted_type;
  int32_t scale;
  int32_t precision;
  int32_t field_id;
  LogicalType logicalType;

  _SchemaElement__isset __isset;

  void __set_type(const Type::type val);

  void __set_type_length(const int32_t val);

  void __set_repetition_type(const FieldRepetitionType::type val);

  void __set_name(const std::string& val);

  void __set_num_children(const int32_t val);

  void __set_converted_type(const ConvertedType::type val);

  void __set_scale(const int32_t val);

  void __set_precision(const int32_t val);

  void __set_field_id(const int32_t val);

  void __set_logicalType(const LogicalType& val);

  bool operator == (const SchemaElement & rhs) const
  {
    if (__isset.type != rhs.__isset.type)
      return false;
    else if (__isset.type && !(type == rhs.type))
      return false;
    if (__isset.type_length != rhs.__isset.type_length)
      return false;
    else if (__isset.type_length && !(type_length == rhs.type_length))
      return false;
    if (__isset.repetition_type != rhs.__isset.repetition_type)
      return false;
    else if (__isset.repetition_type && !(repetition_type == rhs.repetition_type))
      return false;
    if (!(name == rhs.name))
      return false;
    if (__isset.num_children != rhs.__isset.num_children)
      return false;
    else if (__isset.num_children && !(num_children == rhs.num_children))
      return false;
    if (__isset.converted_type != rhs.__isset.converted_type)
      return false;
    else if (__isset.converted_type && !(converted_type == rhs.converted_type))
      return false;
    if (__isset.scale != rhs.__isset.scale)
      return false;
    else if (__isset.scale && !(scale == rhs.scale))
      return false;
    if (__isset.precision != rhs.__isset.precision)
      return false;
    else if (__isset.precision && !(precision == rhs.precision))
      return false;
    if (__isset.field_id != rhs.__isset.field_id)
      return false;
    else if (__isset.field_id && !(field_id == rhs.field_id))
      return false;
    if (__isset.logicalType != rhs.__isset.logicalType)
      return false;
    else if (__isset.logicalType && !(logicalType == rhs.logicalType))
      return false;
    return true;
  }
  bool operator != (const SchemaElement &rhs) const {
    return !(*this == rhs);
  }

  bool operator < (const SchemaElement & ) const;

  uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
  uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;

  virtual void printTo(std::ostream& out) const;
};

void swap(SchemaElement &a, SchemaElement &b);

std::ostream& operator<<(std::ostream& out, const SchemaElement& obj);

typedef struct _DataPageHeader__isset {
  _DataPageHeader__isset() : statistics(false) {}
  bool statistics :1;
} _DataPageHeader__isset;

class DataPageHeader : public virtual ::apache::thrift::TBase {
 public:

  DataPageHeader(const DataPageHeader&);
  DataPageHeader& operator=(const DataPageHeader&);
  DataPageHeader() : num_values(0), encoding((Encoding::type)0), definition_level_encoding((Encoding::type)0), repetition_level_encoding((Encoding::type)0) {
  }

  virtual ~DataPageHeader() throw();
  int32_t num_values;
  Encoding::type encoding;
  Encoding::type definition_level_encoding;
  Encoding::type repetition_level_encoding;
  Statistics statistics;

  _DataPageHeader__isset __isset;

  void __set_num_values(const int32_t val);

  void __set_encoding(const Encoding::type val);

  void __set_definition_level_encoding(const Encoding::type val);

  void __set_repetition_level_encoding(const Encoding::type val);

  void __set_statistics(const Statistics& val);

  bool operator == (const DataPageHeader & rhs) const
  {
    if (!(num_values == rhs.num_values))
      return false;
    if (!(encoding == rhs.encoding))
      return false;
    if (!(definition_level_encoding == rhs.definition_level_encoding))
      return false;
    if (!(repetition_level_encoding == rhs.repetition_level_encoding))
      return false;
    if (__isset.statistics != rhs.__isset.statistics)
      return false;
    else if (__isset.statistics && !(statistics == rhs.statistics))
      return false;
    return true;
  }
  bool operator != (const DataPageHeader &rhs) const {
    return !(*this == rhs);
  }

  bool operator < (const DataPageHeader & ) const;

  uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
  uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;

  virtual void printTo(std::ostream& out) const;
};

void swap(DataPageHeader &a, DataPageHeader &b);

std::ostream& operator<<(std::ostream& out, const DataPageHeader& obj);


class IndexPageHeader : public virtual ::apache::thrift::TBase {
 public:

  IndexPageHeader(const IndexPageHeader&);
  IndexPageHeader& operator=(const IndexPageHeader&);
  IndexPageHeader() {
  }

  virtual ~IndexPageHeader() throw();

  bool operator == (const IndexPageHeader & /* rhs */) const
  {
    return true;
  }
  bool operator != (const IndexPageHeader &rhs) const {
    return !(*this == rhs);
  }

  bool operator < (const IndexPageHeader & ) const;

  uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
  uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;

  virtual void printTo(std::ostream& out) const;
};

void swap(IndexPageHeader &a, IndexPageHeader &b);

std::ostream& operator<<(std::ostream& out, const IndexPageHeader& obj);

typedef struct _DictionaryPageHeader__isset {
  _DictionaryPageHeader__isset() : is_sorted(false) {}
  bool is_sorted :1;
} _DictionaryPageHeader__isset;

class DictionaryPageHeader : public virtual ::apache::thrift::TBase {
 public:

  DictionaryPageHeader(const DictionaryPageHeader&);
  DictionaryPageHeader& operator=(const DictionaryPageHeader&);
  DictionaryPageHeader() : num_values(0), encoding((Encoding::type)0), is_sorted(0) {
  }

  virtual ~DictionaryPageHeader() throw();
  int32_t num_values;
  Encoding::type encoding;
  bool is_sorted;

  _DictionaryPageHeader__isset __isset;

  void __set_num_values(const int32_t val);

  void __set_encoding(const Encoding::type val);

  void __set_is_sorted(const bool val);

  bool operator == (const DictionaryPageHeader & rhs) const
  {
    if (!(num_values == rhs.num_values))
      return false;
    if (!(encoding == rhs.encoding))
      return false;
    if (__isset.is_sorted != rhs.__isset.is_sorted)
      return false;
    else if (__isset.is_sorted && !(is_sorted == rhs.is_sorted))
      return false;
    return true;
  }
  bool operator != (const DictionaryPageHeader &rhs) const {
    return !(*this == rhs);
  }

  bool operator < (const DictionaryPageHeader & ) const;

  uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
  uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;

  virtual void printTo(std::ostream& out) const;
};

void swap(DictionaryPageHeader &a, DictionaryPageHeader &b);

std::ostream& operator<<(std::ostream& out, const DictionaryPageHeader& obj);

typedef struct _DataPageHeaderV2__isset {
  _DataPageHeaderV2__isset() : is_compressed(true), statistics(false) {}
  bool is_compressed :1;
  bool statistics :1;
} _DataPageHeaderV2__isset;

class DataPageHeaderV2 : public virtual ::apache::thrift::TBase {
 public:

  DataPageHeaderV2(const DataPageHeaderV2&);
  DataPageHeaderV2& operator=(const DataPageHeaderV2&);
  DataPageHeaderV2() : num_values(0), num_nulls(0), num_rows(0), encoding((Encoding::type)0), definition_levels_byte_length(0), repetition_levels_byte_length(0), is_compressed(true) {
  }

  virtual ~DataPageHeaderV2() throw();
  int32_t num_values;
  int32_t num_nulls;
  int32_t num_rows;
  Encoding::type encoding;
  int32_t definition_levels_byte_length;
  int32_t repetition_levels_byte_length;
  bool is_compressed;
  Statistics statistics;

  _DataPageHeaderV2__isset __isset;

  void __set_num_values(const int32_t val);

  void __set_num_nulls(const int32_t val);

  void __set_num_rows(const int32_t val);

  void __set_encoding(const Encoding::type val);

  void __set_definition_levels_byte_length(const int32_t val);

  void __set_repetition_levels_byte_length(const int32_t val);

  void __set_is_compressed(const bool val);

  void __set_statistics(const Statistics& val);

  bool operator == (const DataPageHeaderV2 & rhs) const
  {
    if (!(num_values == rhs.num_values))
      return false;
    if (!(num_nulls == rhs.num_nulls))
      return false;
    if (!(num_rows == rhs.num_rows))
      return false;
    if (!(encoding == rhs.encoding))
      return false;
    if (!(definition_levels_byte_length == rhs.definition_levels_byte_length))
      return false;
    if (!(repetition_levels_byte_length == rhs.repetition_levels_byte_length))
      return false;
    if (__isset.is_compressed != rhs.__isset.is_compressed)
      return false;
    else if (__isset.is_compressed && !(is_compressed == rhs.is_compressed))
      return false;
    if (__isset.statistics != rhs.__isset.statistics)
      return false;
    else if (__isset.statistics && !(statistics == rhs.statistics))
      return false;
    return true;
  }
  bool operator != (const DataPageHeaderV2 &rhs) const {
    return !(*this == rhs);
  }

  bool operator < (const DataPageHeaderV2 & ) const;

  uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
  uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;

  virtual void printTo(std::ostream& out) const;
};

void swap(DataPageHeaderV2 &a, DataPageHeaderV2 &b);

std::ostream& operator<<(std::ostream& out, const DataPageHeaderV2& obj);

typedef struct _PageHeader__isset {
  _PageHeader__isset() : crc(false), data_page_header(false), index_page_header(false), dictionary_page_header(false), data_page_header_v2(false) {}
  bool crc :1;
  bool data_page_header :1;
  bool index_page_header :1;
  bool dictionary_page_header :1;
  bool data_page_header_v2 :1;
} _PageHeader__isset;

class PageHeader : public virtual ::apache::thrift::TBase {
 public:

  PageHeader(const PageHeader&);
  PageHeader& operator=(const PageHeader&);
  PageHeader() : type((PageType::type)0), uncompressed_page_size(0), compressed_page_size(0), crc(0) {
  }

  virtual ~PageHeader() throw();
  PageType::type type;
  int32_t uncompressed_page_size;
  int32_t compressed_page_size;
  int32_t crc;
  DataPageHeader data_page_header;
  IndexPageHeader index_page_header;
  DictionaryPageHeader dictionary_page_header;
  DataPageHeaderV2 data_page_header_v2;

  _PageHeader__isset __isset;

  void __set_type(const PageType::type val);

  void __set_uncompressed_page_size(const int32_t val);

  void __set_compressed_page_size(const int32_t val);

  void __set_crc(const int32_t val);

  void __set_data_page_header(const DataPageHeader& val);

  void __set_index_page_header(const IndexPageHeader& val);

  void __set_dictionary_page_header(const DictionaryPageHeader& val);

  void __set_data_page_header_v2(const DataPageHeaderV2& val);

  bool operator == (const PageHeader & rhs) const
  {
    if (!(type == rhs.type))
      return false;
    if (!(uncompressed_page_size == rhs.uncompressed_page_size))
      return false;
    if (!(compressed_page_size == rhs.compressed_page_size))
      return false;
    if (__isset.crc != rhs.__isset.crc)
      return false;
    else if (__isset.crc && !(crc == rhs.crc))
      return false;
    if (__isset.data_page_header != rhs.__isset.data_page_header)
      return false;
    else if (__isset.data_page_header && !(data_page_header == rhs.data_page_header))
      return false;
    if (__isset.index_page_header != rhs.__isset.index_page_header)
      return false;
    else if (__isset.index_page_header && !(index_page_header == rhs.index_page_header))
      return false;
    if (__isset.dictionary_page_header != rhs.__isset.dictionary_page_header)
      return false;
    else if (__isset.dictionary_page_header && !(dictionary_page_header == rhs.dictionary_page_header))
      return false;
    if (__isset.data_page_header_v2 != rhs.__isset.data_page_header_v2)
      return false;
    else if (__isset.data_page_header_v2 && !(data_page_header_v2 == rhs.data_page_header_v2))
      return false;
    return true;
  }
  bool operator != (const PageHeader &rhs) const {
    return !(*this == rhs);
  }

  bool operator < (const PageHeader & ) const;

  uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
  uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;

  virtual void printTo(std::ostream& out) const;
};

void swap(PageHeader &a, PageHeader &b);

std::ostream& operator<<(std::ostream& out, const PageHeader& obj);

typedef struct _KeyValue__isset {
  _KeyValue__isset() : value(false) {}
  bool value :1;
} _KeyValue__isset;

class KeyValue : public virtual ::apache::thrift::TBase {
 public:

  KeyValue(const KeyValue&);
  KeyValue& operator=(const KeyValue&);
  KeyValue() : key(), value() {
  }

  virtual ~KeyValue() throw();
  std::string key;
  std::string value;

  _KeyValue__isset __isset;

  void __set_key(const std::string& val);

  void __set_value(const std::string& val);

  bool operator == (const KeyValue & rhs) const
  {
    if (!(key == rhs.key))
      return false;
    if (__isset.value != rhs.__isset.value)
      return false;
    else if (__isset.value && !(value == rhs.value))
      return false;
    return true;
  }
  bool operator != (const KeyValue &rhs) const {
    return !(*this == rhs);
  }

  bool operator < (const KeyValue & ) const;

  uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
  uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;

  virtual void printTo(std::ostream& out) const;
};

void swap(KeyValue &a, KeyValue &b);

std::ostream& operator<<(std::ostream& out, const KeyValue& obj);


class SortingColumn : public virtual ::apache::thrift::TBase {
 public:

  SortingColumn(const SortingColumn&);
  SortingColumn& operator=(const SortingColumn&);
  SortingColumn() : column_idx(0), descending(0), nulls_first(0) {
  }

  virtual ~SortingColumn() throw();
  int32_t column_idx;
  bool descending;
  bool nulls_first;

  void __set_column_idx(const int32_t val);

  void __set_descending(const bool val);

  void __set_nulls_first(const bool val);

  bool operator == (const SortingColumn & rhs) const
  {
    if (!(column_idx == rhs.column_idx))
      return false;
    if (!(descending == rhs.descending))
      return false;
    if (!(nulls_first == rhs.nulls_first))
      return false;
    return true;
  }
  bool operator != (const SortingColumn &rhs) const {
    return !(*this == rhs);
  }

  bool operator < (const SortingColumn & ) const;

  uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
  uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;

  virtual void printTo(std::ostream& out) const;
};

void swap(SortingColumn &a, SortingColumn &b);

std::ostream& operator<<(std::ostream& out, const SortingColumn& obj);


class PageEncodingStats : public virtual ::apache::thrift::TBase {
 public:

  PageEncodingStats(const PageEncodingStats&);
  PageEncodingStats& operator=(const PageEncodingStats&);
  PageEncodingStats() : page_type((PageType::type)0), encoding((Encoding::type)0), count(0) {
  }

  virtual ~PageEncodingStats() throw();
  PageType::type page_type;
  Encoding::type encoding;
  int32_t count;

  void __set_page_type(const PageType::type val);

  void __set_encoding(const Encoding::type val);

  void __set_count(const int32_t val);

  bool operator == (const PageEncodingStats & rhs) const
  {
    if (!(page_type == rhs.page_type))
      return false;
    if (!(encoding == rhs.encoding))
      return false;
    if (!(count == rhs.count))
      return false;
    return true;
  }
  bool operator != (const PageEncodingStats &rhs) const {
    return !(*this == rhs);
  }

  bool operator < (const PageEncodingStats & ) const;

  uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
  uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;

  virtual void printTo(std::ostream& out) const;
};

void swap(PageEncodingStats &a, PageEncodingStats &b);

std::ostream& operator<<(std::ostream& out, const PageEncodingStats& obj);

typedef struct _ColumnMetaData__isset {
  _ColumnMetaData__isset() : key_value_metadata(false), index_page_offset(false), dictionary_page_offset(false), statistics(false), encoding_stats(false) {}
  bool key_value_metadata :1;
  bool index_page_offset :1;
  bool dictionary_page_offset :1;
  bool statistics :1;
  bool encoding_stats :1;
} _ColumnMetaData__isset;

class ColumnMetaData : public virtual ::apache::thrift::TBase {
 public:

  ColumnMetaData(const ColumnMetaData&);
  ColumnMetaData& operator=(const ColumnMetaData&);
  ColumnMetaData() : type((Type::type)0), codec((CompressionCodec::type)0), num_values(0), total_uncompressed_size(0), total_compressed_size(0), data_page_offset(0), index_page_offset(0), dictionary_page_offset(0) {
  }

  virtual ~ColumnMetaData() throw();
  Type::type type;
  std::vector<Encoding::type>  encodings;
  std::vector<std::string>  path_in_schema;
  CompressionCodec::type codec;
  int64_t num_values;
  int64_t total_uncompressed_size;
  int64_t total_compressed_size;
  std::vector<KeyValue>  key_value_metadata;
  int64_t data_page_offset;
  int64_t index_page_offset;
  int64_t dictionary_page_offset;
  Statistics statistics;
  std::vector<PageEncodingStats>  encoding_stats;

  _ColumnMetaData__isset __isset;

  void __set_type(const Type::type val);

  void __set_encodings(const std::vector<Encoding::type> & val);

  void __set_path_in_schema(const std::vector<std::string> & val);

  void __set_codec(const CompressionCodec::type val);

  void __set_num_values(const int64_t val);

  void __set_total_uncompressed_size(const int64_t val);

  void __set_total_compressed_size(const int64_t val);

  void __set_key_value_metadata(const std::vector<KeyValue> & val);

  void __set_data_page_offset(const int64_t val);

  void __set_index_page_offset(const int64_t val);

  void __set_dictionary_page_offset(const int64_t val);

  void __set_statistics(const Statistics& val);

  void __set_encoding_stats(const std::vector<PageEncodingStats> & val);

  bool operator == (const ColumnMetaData & rhs) const
  {
    if (!(type == rhs.type))
      return false;
    if (!(encodings == rhs.encodings))
      return false;
    if (!(path_in_schema == rhs.path_in_schema))
      return false;
    if (!(codec == rhs.codec))
      return false;
    if (!(num_values == rhs.num_values))
      return false;
    if (!(total_uncompressed_size == rhs.total_uncompressed_size))
      return false;
    if (!(total_compressed_size == rhs.total_compressed_size))
      return false;
    if (__isset.key_value_metadata != rhs.__isset.key_value_metadata)
      return false;
    else if (__isset.key_value_metadata && !(key_value_metadata == rhs.key_value_metadata))
      return false;
    if (!(data_page_offset == rhs.data_page_offset))
      return false;
    if (__isset.index_page_offset != rhs.__isset.index_page_offset)
      return false;
    else if (__isset.index_page_offset && !(index_page_offset == rhs.index_page_offset))
      return false;
    if (__isset.dictionary_page_offset != rhs.__isset.dictionary_page_offset)
      return false;
    else if (__isset.dictionary_page_offset && !(dictionary_page_offset == rhs.dictionary_page_offset))
      return false;
    if (__isset.statistics != rhs.__isset.statistics)
      return false;
    else if (__isset.statistics && !(statistics == rhs.statistics))
      return false;
    if (__isset.encoding_stats != rhs.__isset.encoding_stats)
      return false;
    else if (__isset.encoding_stats && !(encoding_stats == rhs.encoding_stats))
      return false;
    return true;
  }
  bool operator != (const ColumnMetaData &rhs) const {
    return !(*this == rhs);
  }

  bool operator < (const ColumnMetaData & ) const;

  uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
  uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;

  virtual void printTo(std::ostream& out) const;
};

void swap(ColumnMetaData &a, ColumnMetaData &b);

std::ostream& operator<<(std::ostream& out, const ColumnMetaData& obj);


class EncryptionWithFooterKey : public virtual ::apache::thrift::TBase {
 public:

  EncryptionWithFooterKey(const EncryptionWithFooterKey&);
  EncryptionWithFooterKey& operator=(const EncryptionWithFooterKey&);
  EncryptionWithFooterKey() {
  }

  virtual ~EncryptionWithFooterKey() throw();

  bool operator == (const EncryptionWithFooterKey & /* rhs */) const
  {
    return true;
  }
  bool operator != (const EncryptionWithFooterKey &rhs) const {
    return !(*this == rhs);
  }

  bool operator < (const EncryptionWithFooterKey & ) const;

  uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
  uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;

  virtual void printTo(std::ostream& out) const;
};

void swap(EncryptionWithFooterKey &a, EncryptionWithFooterKey &b);

std::ostream& operator<<(std::ostream& out, const EncryptionWithFooterKey& obj);

typedef struct _EncryptionWithColumnKey__isset {
  _EncryptionWithColumnKey__isset() : key_metadata(false) {}
  bool key_metadata :1;
} _EncryptionWithColumnKey__isset;

class EncryptionWithColumnKey : public virtual ::apache::thrift::TBase {
 public:

  EncryptionWithColumnKey(const EncryptionWithColumnKey&);
  EncryptionWithColumnKey& operator=(const EncryptionWithColumnKey&);
  EncryptionWithColumnKey() : key_metadata() {
  }

  virtual ~EncryptionWithColumnKey() throw();
  std::vector<std::string>  path_in_schema;
  std::string key_metadata;

  _EncryptionWithColumnKey__isset __isset;

  void __set_path_in_schema(const std::vector<std::string> & val);

  void __set_key_metadata(const std::string& val);

  bool operator == (const EncryptionWithColumnKey & rhs) const
  {
    if (!(path_in_schema == rhs.path_in_schema))
      return false;
    if (__isset.key_metadata != rhs.__isset.key_metadata)
      return false;
    else if (__isset.key_metadata && !(key_metadata == rhs.key_metadata))
      return false;
    return true;
  }
  bool operator != (const EncryptionWithColumnKey &rhs) const {
    return !(*this == rhs);
  }

  bool operator < (const EncryptionWithColumnKey & ) const;

  uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
  uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;

  virtual void printTo(std::ostream& out) const;
};

void swap(EncryptionWithColumnKey &a, EncryptionWithColumnKey &b);

std::ostream& operator<<(std::ostream& out, const EncryptionWithColumnKey& obj);

typedef struct _ColumnCryptoMetaData__isset {
  _ColumnCryptoMetaData__isset() : ENCRYPTION_WITH_FOOTER_KEY(false), ENCRYPTION_WITH_COLUMN_KEY(false) {}
  bool ENCRYPTION_WITH_FOOTER_KEY :1;
  bool ENCRYPTION_WITH_COLUMN_KEY :1;
} _ColumnCryptoMetaData__isset;

class ColumnCryptoMetaData : public virtual ::apache::thrift::TBase {
 public:

  ColumnCryptoMetaData(const ColumnCryptoMetaData&);
  ColumnCryptoMetaData& operator=(const ColumnCryptoMetaData&);
  ColumnCryptoMetaData() {
  }

  virtual ~ColumnCryptoMetaData() throw();
  EncryptionWithFooterKey ENCRYPTION_WITH_FOOTER_KEY;
  EncryptionWithColumnKey ENCRYPTION_WITH_COLUMN_KEY;

  _ColumnCryptoMetaData__isset __isset;

  void __set_ENCRYPTION_WITH_FOOTER_KEY(const EncryptionWithFooterKey& val);

  void __set_ENCRYPTION_WITH_COLUMN_KEY(const EncryptionWithColumnKey& val);

  bool operator == (const ColumnCryptoMetaData & rhs) const
  {
    if (__isset.ENCRYPTION_WITH_FOOTER_KEY != rhs.__isset.ENCRYPTION_WITH_FOOTER_KEY)
      return false;
    else if (__isset.ENCRYPTION_WITH_FOOTER_KEY && !(ENCRYPTION_WITH_FOOTER_KEY == rhs.ENCRYPTION_WITH_FOOTER_KEY))
      return false;
    if (__isset.ENCRYPTION_WITH_COLUMN_KEY != rhs.__isset.ENCRYPTION_WITH_COLUMN_KEY)
      return false;
    else if (__isset.ENCRYPTION_WITH_COLUMN_KEY && !(ENCRYPTION_WITH_COLUMN_KEY == rhs.ENCRYPTION_WITH_COLUMN_KEY))
      return false;
    return true;
  }
  bool operator != (const ColumnCryptoMetaData &rhs) const {
    return !(*this == rhs);
  }

  bool operator < (const ColumnCryptoMetaData & ) const;

  uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
  uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;

  virtual void printTo(std::ostream& out) const;
};

void swap(ColumnCryptoMetaData &a, ColumnCryptoMetaData &b);

std::ostream& operator<<(std::ostream& out, const ColumnCryptoMetaData& obj);

typedef struct _ColumnChunk__isset {
  _ColumnChunk__isset() : file_path(false), meta_data(false), offset_index_offset(false), offset_index_length(false), column_index_offset(false), column_index_length(false), crypto_metadata(false), encrypted_column_metadata(false) {}
  bool file_path :1;
  bool meta_data :1;
  bool offset_index_offset :1;
  bool offset_index_length :1;
  bool column_index_offset :1;
  bool column_index_length :1;
  bool crypto_metadata :1;
  bool encrypted_column_metadata :1;
} _ColumnChunk__isset;

class ColumnChunk : public virtual ::apache::thrift::TBase {
 public:

  ColumnChunk(const ColumnChunk&);
  ColumnChunk& operator=(const ColumnChunk&);
  ColumnChunk() : file_path(), file_offset(0), offset_index_offset(0), offset_index_length(0), column_index_offset(0), column_index_length(0), encrypted_column_metadata() {
  }

  virtual ~ColumnChunk() throw();
  std::string file_path;
  int64_t file_offset;
  ColumnMetaData meta_data;
  int64_t offset_index_offset;
  int32_t offset_index_length;
  int64_t column_index_offset;
  int32_t column_index_length;
  ColumnCryptoMetaData crypto_metadata;
  std::string encrypted_column_metadata;

  _ColumnChunk__isset __isset;

  void __set_file_path(const std::string& val);

  void __set_file_offset(const int64_t val);

  void __set_meta_data(const ColumnMetaData& val);

  void __set_offset_index_offset(const int64_t val);

  void __set_offset_index_length(const int32_t val);

  void __set_column_index_offset(const int64_t val);

  void __set_column_index_length(const int32_t val);

  void __set_crypto_metadata(const ColumnCryptoMetaData& val);

  void __set_encrypted_column_metadata(const std::string& val);

  bool operator == (const ColumnChunk & rhs) const
  {
    if (__isset.file_path != rhs.__isset.file_path)
      return false;
    else if (__isset.file_path && !(file_path == rhs.file_path))
      return false;
    if (!(file_offset == rhs.file_offset))
      return false;
    if (__isset.meta_data != rhs.__isset.meta_data)
      return false;
    else if (__isset.meta_data && !(meta_data == rhs.meta_data))
      return false;
    if (__isset.offset_index_offset != rhs.__isset.offset_index_offset)
      return false;
    else if (__isset.offset_index_offset && !(offset_index_offset == rhs.offset_index_offset))
      return false;
    if (__isset.offset_index_length != rhs.__isset.offset_index_length)
      return false;
    else if (__isset.offset_index_length && !(offset_index_length == rhs.offset_index_length))
      return false;
    if (__isset.column_index_offset != rhs.__isset.column_index_offset)
      return false;
    else if (__isset.column_index_offset && !(column_index_offset == rhs.column_index_offset))
      return false;
    if (__isset.column_index_length != rhs.__isset.column_index_length)
      return false;
    else if (__isset.column_index_length && !(column_index_length == rhs.column_index_length))
      return false;
    if (__isset.crypto_metadata != rhs.__isset.crypto_metadata)
      return false;
    else if (__isset.crypto_metadata && !(crypto_metadata == rhs.crypto_metadata))
      return false;
    if (__isset.encrypted_column_metadata != rhs.__isset.encrypted_column_metadata)
      return false;
    else if (__isset.encrypted_column_metadata && !(encrypted_column_metadata == rhs.encrypted_column_metadata))
      return false;
    return true;
  }
  bool operator != (const ColumnChunk &rhs) const {
    return !(*this == rhs);
  }

  bool operator < (const ColumnChunk & ) const;

  uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
  uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;

  virtual void printTo(std::ostream& out) const;
};

void swap(ColumnChunk &a, ColumnChunk &b);

std::ostream& operator<<(std::ostream& out, const ColumnChunk& obj);

typedef struct _RowGroup__isset {
  _RowGroup__isset() : sorting_columns(false), file_offset(false), total_compressed_size(false), ordinal(false) {}
  bool sorting_columns :1;
  bool file_offset :1;
  bool total_compressed_size :1;
  bool ordinal :1;
} _RowGroup__isset;

class RowGroup : public virtual ::apache::thrift::TBase {
 public:

  RowGroup(const RowGroup&);
  RowGroup& operator=(const RowGroup&);
  RowGroup() : total_byte_size(0), num_rows(0), file_offset(0), total_compressed_size(0), ordinal(0) {
  }

  virtual ~RowGroup() throw();
  std::vector<ColumnChunk>  columns;
  int64_t total_byte_size;
  int64_t num_rows;
  std::vector<SortingColumn>  sorting_columns;
  int64_t file_offset;
  int64_t total_compressed_size;
  int16_t ordinal;

  _RowGroup__isset __isset;

  void __set_columns(const std::vector<ColumnChunk> & val);

  void __set_total_byte_size(const int64_t val);

  void __set_num_rows(const int64_t val);

  void __set_sorting_columns(const std::vector<SortingColumn> & val);

  void __set_file_offset(const int64_t val);

  void __set_total_compressed_size(const int64_t val);

  void __set_ordinal(const int16_t val);

  bool operator == (const RowGroup & rhs) const
  {
    if (!(columns == rhs.columns))
      return false;
    if (!(total_byte_size == rhs.total_byte_size))
      return false;
    if (!(num_rows == rhs.num_rows))
      return false;
    if (__isset.sorting_columns != rhs.__isset.sorting_columns)
      return false;
    else if (__isset.sorting_columns && !(sorting_columns == rhs.sorting_columns))
      return false;
    if (__isset.file_offset != rhs.__isset.file_offset)
      return false;
    else if (__isset.file_offset && !(file_offset == rhs.file_offset))
      return false;
    if (__isset.total_compressed_size != rhs.__isset.total_compressed_size)
      return false;
    else if (__isset.total_compressed_size && !(total_compressed_size == rhs.total_compressed_size))
      return false;
    if (__isset.ordinal != rhs.__isset.ordinal)
      return false;
    else if (__isset.ordinal && !(ordinal == rhs.ordinal))
      return false;
    return true;
  }
  bool operator != (const RowGroup &rhs) const {
    return !(*this == rhs);
  }

  bool operator < (const RowGroup & ) const;

  uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
  uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;

  virtual void printTo(std::ostream& out) const;
};

void swap(RowGroup &a, RowGroup &b);

std::ostream& operator<<(std::ostream& out, const RowGroup& obj);


class TypeDefinedOrder : public virtual ::apache::thrift::TBase {
 public:

  TypeDefinedOrder(const TypeDefinedOrder&);
  TypeDefinedOrder& operator=(const TypeDefinedOrder&);
  TypeDefinedOrder() {
  }

  virtual ~TypeDefinedOrder() throw();

  bool operator == (const TypeDefinedOrder & /* rhs */) const
  {
    return true;
  }
  bool operator != (const TypeDefinedOrder &rhs) const {
    return !(*this == rhs);
  }

  bool operator < (const TypeDefinedOrder & ) const;

  uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
  uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;

  virtual void printTo(std::ostream& out) const;
};

void swap(TypeDefinedOrder &a, TypeDefinedOrder &b);

std::ostream& operator<<(std::ostream& out, const TypeDefinedOrder& obj);

typedef struct _ColumnOrder__isset {
  _ColumnOrder__isset() : TYPE_ORDER(false) {}
  bool TYPE_ORDER :1;
} _ColumnOrder__isset;

class ColumnOrder : public virtual ::apache::thrift::TBase {
 public:

  ColumnOrder(const ColumnOrder&);
  ColumnOrder& operator=(const ColumnOrder&);
  ColumnOrder() {
  }

  virtual ~ColumnOrder() throw();
  TypeDefinedOrder TYPE_ORDER;

  _ColumnOrder__isset __isset;

  void __set_TYPE_ORDER(const TypeDefinedOrder& val);

  bool operator == (const ColumnOrder & rhs) const
  {
    if (__isset.TYPE_ORDER != rhs.__isset.TYPE_ORDER)
      return false;
    else if (__isset.TYPE_ORDER && !(TYPE_ORDER == rhs.TYPE_ORDER))
      return false;
    return true;
  }
  bool operator != (const ColumnOrder &rhs) const {
    return !(*this == rhs);
  }

  bool operator < (const ColumnOrder & ) const;

  uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
  uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;

  virtual void printTo(std::ostream& out) const;
};

void swap(ColumnOrder &a, ColumnOrder &b);

std::ostream& operator<<(std::ostream& out, const ColumnOrder& obj);


class PageLocation : public virtual ::apache::thrift::TBase {
 public:

  PageLocation(const PageLocation&);
  PageLocation& operator=(const PageLocation&);
  PageLocation() : offset(0), compressed_page_size(0), first_row_index(0) {
  }

  virtual ~PageLocation() throw();
  int64_t offset;
  int32_t compressed_page_size;
  int64_t first_row_index;

  void __set_offset(const int64_t val);

  void __set_compressed_page_size(const int32_t val);

  void __set_first_row_index(const int64_t val);

  bool operator == (const PageLocation & rhs) const
  {
    if (!(offset == rhs.offset))
      return false;
    if (!(compressed_page_size == rhs.compressed_page_size))
      return false;
    if (!(first_row_index == rhs.first_row_index))
      return false;
    return true;
  }
  bool operator != (const PageLocation &rhs) const {
    return !(*this == rhs);
  }

  bool operator < (const PageLocation & ) const;

  uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
  uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;

  virtual void printTo(std::ostream& out) const;
};

void swap(PageLocation &a, PageLocation &b);

std::ostream& operator<<(std::ostream& out, const PageLocation& obj);


class OffsetIndex : public virtual ::apache::thrift::TBase {
 public:

  OffsetIndex(const OffsetIndex&);
  OffsetIndex& operator=(const OffsetIndex&);
  OffsetIndex() {
  }

  virtual ~OffsetIndex() throw();
  std::vector<PageLocation>  page_locations;

  void __set_page_locations(const std::vector<PageLocation> & val);

  bool operator == (const OffsetIndex & rhs) const
  {
    if (!(page_locations == rhs.page_locations))
      return false;
    return true;
  }
  bool operator != (const OffsetIndex &rhs) const {
    return !(*this == rhs);
  }

  bool operator < (const OffsetIndex & ) const;

  uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
  uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;

  virtual void printTo(std::ostream& out) const;
};

void swap(OffsetIndex &a, OffsetIndex &b);

std::ostream& operator<<(std::ostream& out, const OffsetIndex& obj);

typedef struct _ColumnIndex__isset {
  _ColumnIndex__isset() : null_counts(false) {}
  bool null_counts :1;
} _ColumnIndex__isset;

class ColumnIndex : public virtual ::apache::thrift::TBase {
 public:

  ColumnIndex(const ColumnIndex&);
  ColumnIndex& operator=(const ColumnIndex&);
  ColumnIndex() : boundary_order((BoundaryOrder::type)0) {
  }

  virtual ~ColumnIndex() throw();
  std::vector<bool>  null_pages;
  std::vector<std::string>  min_values;
  std::vector<std::string>  max_values;
  BoundaryOrder::type boundary_order;
  std::vector<int64_t>  null_counts;

  _ColumnIndex__isset __isset;

  void __set_null_pages(const std::vector<bool> & val);

  void __set_min_values(const std::vector<std::string> & val);

  void __set_max_values(const std::vector<std::string> & val);

  void __set_boundary_order(const BoundaryOrder::type val);

  void __set_null_counts(const std::vector<int64_t> & val);

  bool operator == (const ColumnIndex & rhs) const
  {
    if (!(null_pages == rhs.null_pages))
      return false;
    if (!(min_values == rhs.min_values))
      return false;
    if (!(max_values == rhs.max_values))
      return false;
    if (!(boundary_order == rhs.boundary_order))
      return false;
    if (__isset.null_counts != rhs.__isset.null_counts)
      return false;
    else if (__isset.null_counts && !(null_counts == rhs.null_counts))
      return false;
    return true;
  }
  bool operator != (const ColumnIndex &rhs) const {
    return !(*this == rhs);
  }

  bool operator < (const ColumnIndex & ) const;

  uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
  uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;

  virtual void printTo(std::ostream& out) const;
};

void swap(ColumnIndex &a, ColumnIndex &b);

std::ostream& operator<<(std::ostream& out, const ColumnIndex& obj);

typedef struct _AesGcmV1__isset {
  _AesGcmV1__isset() : aad_prefix(false), aad_file_unique(false), supply_aad_prefix(false) {}
  bool aad_prefix :1;
  bool aad_file_unique :1;
  bool supply_aad_prefix :1;
} _AesGcmV1__isset;

class AesGcmV1 : public virtual ::apache::thrift::TBase {
 public:

  AesGcmV1(const AesGcmV1&);
  AesGcmV1& operator=(const AesGcmV1&);
  AesGcmV1() : aad_prefix(), aad_file_unique(), supply_aad_prefix(0) {
  }

  virtual ~AesGcmV1() throw();
  std::string aad_prefix;
  std::string aad_file_unique;
  bool supply_aad_prefix;

  _AesGcmV1__isset __isset;

  void __set_aad_prefix(const std::string& val);

  void __set_aad_file_unique(const std::string& val);

  void __set_supply_aad_prefix(const bool val);

  bool operator == (const AesGcmV1 & rhs) const
  {
    if (__isset.aad_prefix != rhs.__isset.aad_prefix)
      return false;
    else if (__isset.aad_prefix && !(aad_prefix == rhs.aad_prefix))
      return false;
    if (__isset.aad_file_unique != rhs.__isset.aad_file_unique)
      return false;
    else if (__isset.aad_file_unique && !(aad_file_unique == rhs.aad_file_unique))
      return false;
    if (__isset.supply_aad_prefix != rhs.__isset.supply_aad_prefix)
      return false;
    else if (__isset.supply_aad_prefix && !(supply_aad_prefix == rhs.supply_aad_prefix))
      return false;
    return true;
  }
  bool operator != (const AesGcmV1 &rhs) const {
    return !(*this == rhs);
  }

  bool operator < (const AesGcmV1 & ) const;

  uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
  uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;

  virtual void printTo(std::ostream& out) const;
};

void swap(AesGcmV1 &a, AesGcmV1 &b);

std::ostream& operator<<(std::ostream& out, const AesGcmV1& obj);

typedef struct _AesGcmCtrV1__isset {
  _AesGcmCtrV1__isset() : aad_prefix(false), aad_file_unique(false), supply_aad_prefix(false) {}
  bool aad_prefix :1;
  bool aad_file_unique :1;
  bool supply_aad_prefix :1;
} _AesGcmCtrV1__isset;

class AesGcmCtrV1 : public virtual ::apache::thrift::TBase {
 public:

  AesGcmCtrV1(const AesGcmCtrV1&);
  AesGcmCtrV1& operator=(const AesGcmCtrV1&);
  AesGcmCtrV1() : aad_prefix(), aad_file_unique(), supply_aad_prefix(0) {
  }

  virtual ~AesGcmCtrV1() throw();
  std::string aad_prefix;
  std::string aad_file_unique;
  bool supply_aad_prefix;

  _AesGcmCtrV1__isset __isset;

  void __set_aad_prefix(const std::string& val);

  void __set_aad_file_unique(const std::string& val);

  void __set_supply_aad_prefix(const bool val);

  bool operator == (const AesGcmCtrV1 & rhs) const
  {
    if (__isset.aad_prefix != rhs.__isset.aad_prefix)
      return false;
    else if (__isset.aad_prefix && !(aad_prefix == rhs.aad_prefix))
      return false;
    if (__isset.aad_file_unique != rhs.__isset.aad_file_unique)
      return false;
    else if (__isset.aad_file_unique && !(aad_file_unique == rhs.aad_file_unique))
      return false;
    if (__isset.supply_aad_prefix != rhs.__isset.supply_aad_prefix)
      return false;
    else if (__isset.supply_aad_prefix && !(supply_aad_prefix == rhs.supply_aad_prefix))
      return false;
    return true;
  }
  bool operator != (const AesGcmCtrV1 &rhs) const {
    return !(*this == rhs);
  }

  bool operator < (const AesGcmCtrV1 & ) const;

  uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
  uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;

  virtual void printTo(std::ostream& out) const;
};

void swap(AesGcmCtrV1 &a, AesGcmCtrV1 &b);

std::ostream& operator<<(std::ostream& out, const AesGcmCtrV1& obj);

typedef struct _EncryptionAlgorithm__isset {
  _EncryptionAlgorithm__isset() : AES_GCM_V1(false), AES_GCM_CTR_V1(false) {}
  bool AES_GCM_V1 :1;
  bool AES_GCM_CTR_V1 :1;
} _EncryptionAlgorithm__isset;

class EncryptionAlgorithm : public virtual ::apache::thrift::TBase {
 public:

  EncryptionAlgorithm(const EncryptionAlgorithm&);
  EncryptionAlgorithm& operator=(const EncryptionAlgorithm&);
  EncryptionAlgorithm() {
  }

  virtual ~EncryptionAlgorithm() throw();
  AesGcmV1 AES_GCM_V1;
  AesGcmCtrV1 AES_GCM_CTR_V1;

  _EncryptionAlgorithm__isset __isset;

  void __set_AES_GCM_V1(const AesGcmV1& val);

  void __set_AES_GCM_CTR_V1(const AesGcmCtrV1& val);

  bool operator == (const EncryptionAlgorithm & rhs) const
  {
    if (__isset.AES_GCM_V1 != rhs.__isset.AES_GCM_V1)
      return false;
    else if (__isset.AES_GCM_V1 && !(AES_GCM_V1 == rhs.AES_GCM_V1))
      return false;
    if (__isset.AES_GCM_CTR_V1 != rhs.__isset.AES_GCM_CTR_V1)
      return false;
    else if (__isset.AES_GCM_CTR_V1 && !(AES_GCM_CTR_V1 == rhs.AES_GCM_CTR_V1))
      return false;
    return true;
  }
  bool operator != (const EncryptionAlgorithm &rhs) const {
    return !(*this == rhs);
  }

  bool operator < (const EncryptionAlgorithm & ) const;

  uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
  uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;

  virtual void printTo(std::ostream& out) const;
};

void swap(EncryptionAlgorithm &a, EncryptionAlgorithm &b);

std::ostream& operator<<(std::ostream& out, const EncryptionAlgorithm& obj);

typedef struct _FileMetaData__isset {
  _FileMetaData__isset() : key_value_metadata(false), created_by(false), column_orders(false), encryption_algorithm(false), footer_signing_key_metadata(false) {}
  bool key_value_metadata :1;
  bool created_by :1;
  bool column_orders :1;
  bool encryption_algorithm :1;
  bool footer_signing_key_metadata :1;
} _FileMetaData__isset;

class FileMetaData : public virtual ::apache::thrift::TBase {
 public:

  FileMetaData(const FileMetaData&);
  FileMetaData& operator=(const FileMetaData&);
  FileMetaData() : version(0), num_rows(0), created_by(), footer_signing_key_metadata() {
  }

  virtual ~FileMetaData() throw();
  int32_t version;
  std::vector<SchemaElement>  schema;
  int64_t num_rows;
  std::vector<RowGroup>  row_groups;
  std::vector<KeyValue>  key_value_metadata;
  std::string created_by;
  std::vector<ColumnOrder>  column_orders;
  EncryptionAlgorithm encryption_algorithm;
  std::string footer_signing_key_metadata;

  _FileMetaData__isset __isset;

  void __set_version(const int32_t val);

  void __set_schema(const std::vector<SchemaElement> & val);

  void __set_num_rows(const int64_t val);

  void __set_row_groups(const std::vector<RowGroup> & val);

  void __set_key_value_metadata(const std::vector<KeyValue> & val);

  void __set_created_by(const std::string& val);

  void __set_column_orders(const std::vector<ColumnOrder> & val);

  void __set_encryption_algorithm(const EncryptionAlgorithm& val);

  void __set_footer_signing_key_metadata(const std::string& val);

  bool operator == (const FileMetaData & rhs) const
  {
    if (!(version == rhs.version))
      return false;
    if (!(schema == rhs.schema))
      return false;
    if (!(num_rows == rhs.num_rows))
      return false;
    if (!(row_groups == rhs.row_groups))
      return false;
    if (__isset.key_value_metadata != rhs.__isset.key_value_metadata)
      return false;
    else if (__isset.key_value_metadata && !(key_value_metadata == rhs.key_value_metadata))
      return false;
    if (__isset.created_by != rhs.__isset.created_by)
      return false;
    else if (__isset.created_by && !(created_by == rhs.created_by))
      return false;
    if (__isset.column_orders != rhs.__isset.column_orders)
      return false;
    else if (__isset.column_orders && !(column_orders == rhs.column_orders))
      return false;
    if (__isset.encryption_algorithm != rhs.__isset.encryption_algorithm)
      return false;
    else if (__isset.encryption_algorithm && !(encryption_algorithm == rhs.encryption_algorithm))
      return false;
    if (__isset.footer_signing_key_metadata != rhs.__isset.footer_signing_key_metadata)
      return false;
    else if (__isset.footer_signing_key_metadata && !(footer_signing_key_metadata == rhs.footer_signing_key_metadata))
      return false;
    return true;
  }
  bool operator != (const FileMetaData &rhs) const {
    return !(*this == rhs);
  }

  bool operator < (const FileMetaData & ) const;

  uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
  uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;

  virtual void printTo(std::ostream& out) const;
};

void swap(FileMetaData &a, FileMetaData &b);

std::ostream& operator<<(std::ostream& out, const FileMetaData& obj);

typedef struct _FileCryptoMetaData__isset {
  _FileCryptoMetaData__isset() : key_metadata(false) {}
  bool key_metadata :1;
} _FileCryptoMetaData__isset;

class FileCryptoMetaData : public virtual ::apache::thrift::TBase {
 public:

  FileCryptoMetaData(const FileCryptoMetaData&);
  FileCryptoMetaData& operator=(const FileCryptoMetaData&);
  FileCryptoMetaData() : key_metadata() {
  }

  virtual ~FileCryptoMetaData() throw();
  EncryptionAlgorithm encryption_algorithm;
  std::string key_metadata;

  _FileCryptoMetaData__isset __isset;

  void __set_encryption_algorithm(const EncryptionAlgorithm& val);

  void __set_key_metadata(const std::string& val);

  bool operator == (const FileCryptoMetaData & rhs) const
  {
    if (!(encryption_algorithm == rhs.encryption_algorithm))
      return false;
    if (__isset.key_metadata != rhs.__isset.key_metadata)
      return false;
    else if (__isset.key_metadata && !(key_metadata == rhs.key_metadata))
      return false;
    return true;
  }
  bool operator != (const FileCryptoMetaData &rhs) const {
    return !(*this == rhs);
  }

  bool operator < (const FileCryptoMetaData & ) const;

  uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
  uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;

  virtual void printTo(std::ostream& out) const;
};

void swap(FileCryptoMetaData &a, FileCryptoMetaData &b);

std::ostream& operator<<(std::ostream& out, const FileCryptoMetaData& obj);

}} // namespace

#endif

/**
 * Autogenerated by Thrift Compiler (0.11.0)
 *
 * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
 *  @generated
 */
#ifndef parquet_CONSTANTS_H
#define parquet_CONSTANTS_H

// #include "parquet_types.h"

namespace parquet { namespace format {

class parquetConstants {
 public:
  parquetConstants();

};

extern const parquetConstants g_parquet_constants;

}} // namespace

#endif

/**
 * Autogenerated by Thrift Compiler (0.11.0)
 *
 * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
 *  @generated
 */
// #include "parquet_types.h"

#include <algorithm>
#include <ostream>

// #include <thrift/TToString.h>

namespace parquet { namespace format {

int _kTypeValues[] = {
  Type::BOOLEAN,
  Type::INT32,
  Type::INT64,
  Type::INT96,
  Type::FLOAT,
  Type::DOUBLE,
  Type::BYTE_ARRAY,
  Type::FIXED_LEN_BYTE_ARRAY
};
const char* _kTypeNames[] = {
  "BOOLEAN",
  "INT32",
  "INT64",
  "INT96",
  "FLOAT",
  "DOUBLE",
  "BYTE_ARRAY",
  "FIXED_LEN_BYTE_ARRAY"
};
const std::map<int, const char*> _Type_VALUES_TO_NAMES(::apache::thrift::TEnumIterator(8, _kTypeValues, _kTypeNames), ::apache::thrift::TEnumIterator(-1, NULL, NULL));

std::ostream& operator<<(std::ostream& out, const Type::type& val) {
  std::map<int, const char*>::const_iterator it = _Type_VALUES_TO_NAMES.find(val);
  if (it != _Type_VALUES_TO_NAMES.end()) {
    out << it->second;
  } else {
    out << static_cast<int>(val);
  }
  return out;
}

int _kConvertedTypeValues[] = {
  ConvertedType::UTF8,
  ConvertedType::MAP,
  ConvertedType::MAP_KEY_VALUE,
  ConvertedType::LIST,
  ConvertedType::ENUM,
  ConvertedType::DECIMAL,
  ConvertedType::DATE,
  ConvertedType::TIME_MILLIS,
  ConvertedType::TIME_MICROS,
  ConvertedType::TIMESTAMP_MILLIS,
  ConvertedType::TIMESTAMP_MICROS,
  ConvertedType::UINT_8,
  ConvertedType::UINT_16,
  ConvertedType::UINT_32,
  ConvertedType::UINT_64,
  ConvertedType::INT_8,
  ConvertedType::INT_16,
  ConvertedType::INT_32,
  ConvertedType::INT_64,
  ConvertedType::JSON,
  ConvertedType::BSON,
  ConvertedType::INTERVAL
};
const char* _kConvertedTypeNames[] = {
  "UTF8",
  "MAP",
  "MAP_KEY_VALUE",
  "LIST",
  "ENUM",
  "DECIMAL",
  "DATE",
  "TIME_MILLIS",
  "TIME_MICROS",
  "TIMESTAMP_MILLIS",
  "TIMESTAMP_MICROS",
  "UINT_8",
  "UINT_16",
  "UINT_32",
  "UINT_64",
  "INT_8",
  "INT_16",
  "INT_32",
  "INT_64",
  "JSON",
  "BSON",
  "INTERVAL"
};
const std::map<int, const char*> _ConvertedType_VALUES_TO_NAMES(::apache::thrift::TEnumIterator(22, _kConvertedTypeValues, _kConvertedTypeNames), ::apache::thrift::TEnumIterator(-1, NULL, NULL));

std::ostream& operator<<(std::ostream& out, const ConvertedType::type& val) {
  std::map<int, const char*>::const_iterator it = _ConvertedType_VALUES_TO_NAMES.find(val);
  if (it != _ConvertedType_VALUES_TO_NAMES.end()) {
    out << it->second;
  } else {
    out << static_cast<int>(val);
  }
  return out;
}

int _kFieldRepetitionTypeValues[] = {
  FieldRepetitionType::REQUIRED,
  FieldRepetitionType::OPTIONAL,
  FieldRepetitionType::REPEATED
};
const char* _kFieldRepetitionTypeNames[] = {
  "REQUIRED",
  "OPTIONAL",
  "REPEATED"
};
const std::map<int, const char*> _FieldRepetitionType_VALUES_TO_NAMES(::apache::thrift::TEnumIterator(3, _kFieldRepetitionTypeValues, _kFieldRepetitionTypeNames), ::apache::thrift::TEnumIterator(-1, NULL, NULL));

std::ostream& operator<<(std::ostream& out, const FieldRepetitionType::type& val) {
  std::map<int, const char*>::const_iterator it = _FieldRepetitionType_VALUES_TO_NAMES.find(val);
  if (it != _FieldRepetitionType_VALUES_TO_NAMES.end()) {
    out << it->second;
  } else {
    out << static_cast<int>(val);
  }
  return out;
}

int _kEncodingValues[] = {
  Encoding::PLAIN,
  Encoding::PLAIN_DICTIONARY,
  Encoding::RLE,
  Encoding::BIT_PACKED,
  Encoding::DELTA_BINARY_PACKED,
  Encoding::DELTA_LENGTH_BYTE_ARRAY,
  Encoding::DELTA_BYTE_ARRAY,
  Encoding::RLE_DICTIONARY
};
const char* _kEncodingNames[] = {
  "PLAIN",
  "PLAIN_DICTIONARY",
  "RLE",
  "BIT_PACKED",
  "DELTA_BINARY_PACKED",
  "DELTA_LENGTH_BYTE_ARRAY",
  "DELTA_BYTE_ARRAY",
  "RLE_DICTIONARY"
};
const std::map<int, const char*> _Encoding_VALUES_TO_NAMES(::apache::thrift::TEnumIterator(8, _kEncodingValues, _kEncodingNames), ::apache::thrift::TEnumIterator(-1, NULL, NULL));

std::ostream& operator<<(std::ostream& out, const Encoding::type& val) {
  std::map<int, const char*>::const_iterator it = _Encoding_VALUES_TO_NAMES.find(val);
  if (it != _Encoding_VALUES_TO_NAMES.end()) {
    out << it->second;
  } else {
    out << static_cast<int>(val);
  }
  return out;
}

int _kCompressionCodecValues[] = {
  CompressionCodec::UNCOMPRESSED,
  CompressionCodec::SNAPPY,
  CompressionCodec::GZIP,
  CompressionCodec::LZO,
  CompressionCodec::BROTLI,
  CompressionCodec::LZ4,
  CompressionCodec::ZSTD
};
const char* _kCompressionCodecNames[] = {
  "UNCOMPRESSED",
  "SNAPPY",
  "GZIP",
  "LZO",
  "BROTLI",
  "LZ4",
  "ZSTD"
};
const std::map<int, const char*> _CompressionCodec_VALUES_TO_NAMES(::apache::thrift::TEnumIterator(7, _kCompressionCodecValues, _kCompressionCodecNames), ::apache::thrift::TEnumIterator(-1, NULL, NULL));

std::ostream& operator<<(std::ostream& out, const CompressionCodec::type& val) {
  std::map<int, const char*>::const_iterator it = _CompressionCodec_VALUES_TO_NAMES.find(val);
  if (it != _CompressionCodec_VALUES_TO_NAMES.end()) {
    out << it->second;
  } else {
    out << static_cast<int>(val);
  }
  return out;
}

int _kPageTypeValues[] = {
  PageType::DATA_PAGE,
  PageType::INDEX_PAGE,
  PageType::DICTIONARY_PAGE,
  PageType::DATA_PAGE_V2
};
const char* _kPageTypeNames[] = {
  "DATA_PAGE",
  "INDEX_PAGE",
  "DICTIONARY_PAGE",
  "DATA_PAGE_V2"
};
const std::map<int, const char*> _PageType_VALUES_TO_NAMES(::apache::thrift::TEnumIterator(4, _kPageTypeValues, _kPageTypeNames), ::apache::thrift::TEnumIterator(-1, NULL, NULL));

std::ostream& operator<<(std::ostream& out, const PageType::type& val) {
  std::map<int, const char*>::const_iterator it = _PageType_VALUES_TO_NAMES.find(val);
  if (it != _PageType_VALUES_TO_NAMES.end()) {
    out << it->second;
  } else {
    out << static_cast<int>(val);
  }
  return out;
}

int _kBoundaryOrderValues[] = {
  BoundaryOrder::UNORDERED,
  BoundaryOrder::ASCENDING,
  BoundaryOrder::DESCENDING
};
const char* _kBoundaryOrderNames[] = {
  "UNORDERED",
  "ASCENDING",
  "DESCENDING"
};
const std::map<int, const char*> _BoundaryOrder_VALUES_TO_NAMES(::apache::thrift::TEnumIterator(3, _kBoundaryOrderValues, _kBoundaryOrderNames), ::apache::thrift::TEnumIterator(-1, NULL, NULL));

std::ostream& operator<<(std::ostream& out, const BoundaryOrder::type& val) {
  std::map<int, const char*>::const_iterator it = _BoundaryOrder_VALUES_TO_NAMES.find(val);
  if (it != _BoundaryOrder_VALUES_TO_NAMES.end()) {
    out << it->second;
  } else {
    out << static_cast<int>(val);
  }
  return out;
}


Statistics::~Statistics() throw() {
}


void Statistics::__set_max(const std::string& val) {
  this->max = val;
__isset.max = true;
}

void Statistics::__set_min(const std::string& val) {
  this->min = val;
__isset.min = true;
}

void Statistics::__set_null_count(const int64_t val) {
  this->null_count = val;
__isset.null_count = true;
}

void Statistics::__set_distinct_count(const int64_t val) {
  this->distinct_count = val;
__isset.distinct_count = true;
}

void Statistics::__set_max_value(const std::string& val) {
  this->max_value = val;
__isset.max_value = true;
}

void Statistics::__set_min_value(const std::string& val) {
  this->min_value = val;
__isset.min_value = true;
}
std::ostream& operator<<(std::ostream& out, const Statistics& obj)
{
  obj.printTo(out);
  return out;
}


uint32_t Statistics::read(::apache::thrift::protocol::TProtocol* iprot) {

  ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
  uint32_t xfer = 0;
  std::string fname;
  ::apache::thrift::protocol::TType ftype;
  int16_t fid;

  xfer += iprot->readStructBegin(fname);

  using ::apache::thrift::protocol::TProtocolException;


  while (true)
  {
    xfer += iprot->readFieldBegin(fname, ftype, fid);
    if (ftype == ::apache::thrift::protocol::T_STOP) {
      break;
    }
    switch (fid)
    {
      case 1:
        if (ftype == ::apache::thrift::protocol::T_STRING) {
          xfer += iprot->readBinary(this->max);
          this->__isset.max = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 2:
        if (ftype == ::apache::thrift::protocol::T_STRING) {
          xfer += iprot->readBinary(this->min);
          this->__isset.min = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 3:
        if (ftype == ::apache::thrift::protocol::T_I64) {
          xfer += iprot->readI64(this->null_count);
          this->__isset.null_count = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 4:
        if (ftype == ::apache::thrift::protocol::T_I64) {
          xfer += iprot->readI64(this->distinct_count);
          this->__isset.distinct_count = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 5:
        if (ftype == ::apache::thrift::protocol::T_STRING) {
          xfer += iprot->readBinary(this->max_value);
          this->__isset.max_value = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 6:
        if (ftype == ::apache::thrift::protocol::T_STRING) {
          xfer += iprot->readBinary(this->min_value);
          this->__isset.min_value = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      default:
        xfer += iprot->skip(ftype);
        break;
    }
    xfer += iprot->readFieldEnd();
  }

  xfer += iprot->readStructEnd();

  return xfer;
}

uint32_t Statistics::write(::apache::thrift::protocol::TProtocol* oprot) const {
  uint32_t xfer = 0;
  ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
  xfer += oprot->writeStructBegin("Statistics");

  if (this->__isset.max) {
    xfer += oprot->writeFieldBegin("max", ::apache::thrift::protocol::T_STRING, 1);
    xfer += oprot->writeBinary(this->max);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.min) {
    xfer += oprot->writeFieldBegin("min", ::apache::thrift::protocol::T_STRING, 2);
    xfer += oprot->writeBinary(this->min);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.null_count) {
    xfer += oprot->writeFieldBegin("null_count", ::apache::thrift::protocol::T_I64, 3);
    xfer += oprot->writeI64(this->null_count);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.distinct_count) {
    xfer += oprot->writeFieldBegin("distinct_count", ::apache::thrift::protocol::T_I64, 4);
    xfer += oprot->writeI64(this->distinct_count);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.max_value) {
    xfer += oprot->writeFieldBegin("max_value", ::apache::thrift::protocol::T_STRING, 5);
    xfer += oprot->writeBinary(this->max_value);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.min_value) {
    xfer += oprot->writeFieldBegin("min_value", ::apache::thrift::protocol::T_STRING, 6);
    xfer += oprot->writeBinary(this->min_value);
    xfer += oprot->writeFieldEnd();
  }
  xfer += oprot->writeFieldStop();
  xfer += oprot->writeStructEnd();
  return xfer;
}

void swap(Statistics &a, Statistics &b) {
  using ::std::swap;
  swap(a.max, b.max);
  swap(a.min, b.min);
  swap(a.null_count, b.null_count);
  swap(a.distinct_count, b.distinct_count);
  swap(a.max_value, b.max_value);
  swap(a.min_value, b.min_value);
  swap(a.__isset, b.__isset);
}

Statistics::Statistics(const Statistics& other0) {
  max = other0.max;
  min = other0.min;
  null_count = other0.null_count;
  distinct_count = other0.distinct_count;
  max_value = other0.max_value;
  min_value = other0.min_value;
  __isset = other0.__isset;
}
Statistics& Statistics::operator=(const Statistics& other1) {
  max = other1.max;
  min = other1.min;
  null_count = other1.null_count;
  distinct_count = other1.distinct_count;
  max_value = other1.max_value;
  min_value = other1.min_value;
  __isset = other1.__isset;
  return *this;
}
void Statistics::printTo(std::ostream& out) const {
  using ::apache::thrift::to_string;
  out << "Statistics(";
  out << "max="; (__isset.max ? (out << to_string(max)) : (out << "<null>"));
  out << ", " << "min="; (__isset.min ? (out << to_string(min)) : (out << "<null>"));
  out << ", " << "null_count="; (__isset.null_count ? (out << to_string(null_count)) : (out << "<null>"));
  out << ", " << "distinct_count="; (__isset.distinct_count ? (out << to_string(distinct_count)) : (out << "<null>"));
  out << ", " << "max_value="; (__isset.max_value ? (out << to_string(max_value)) : (out << "<null>"));
  out << ", " << "min_value="; (__isset.min_value ? (out << to_string(min_value)) : (out << "<null>"));
  out << ")";
}


StringType::~StringType() throw() {
}

std::ostream& operator<<(std::ostream& out, const StringType& obj)
{
  obj.printTo(out);
  return out;
}


uint32_t StringType::read(::apache::thrift::protocol::TProtocol* iprot) {

  ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
  uint32_t xfer = 0;
  std::string fname;
  ::apache::thrift::protocol::TType ftype;
  int16_t fid;

  xfer += iprot->readStructBegin(fname);

  using ::apache::thrift::protocol::TProtocolException;


  while (true)
  {
    xfer += iprot->readFieldBegin(fname, ftype, fid);
    if (ftype == ::apache::thrift::protocol::T_STOP) {
      break;
    }
    xfer += iprot->skip(ftype);
    xfer += iprot->readFieldEnd();
  }

  xfer += iprot->readStructEnd();

  return xfer;
}

uint32_t StringType::write(::apache::thrift::protocol::TProtocol* oprot) const {
  uint32_t xfer = 0;
  ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
  xfer += oprot->writeStructBegin("StringType");

  xfer += oprot->writeFieldStop();
  xfer += oprot->writeStructEnd();
  return xfer;
}

void swap(StringType &a, StringType &b) {
  using ::std::swap;
  (void) a;
  (void) b;
}

StringType::StringType(const StringType& other2) {
  (void) other2;
}
StringType& StringType::operator=(const StringType& other3) {
  (void) other3;
  return *this;
}
void StringType::printTo(std::ostream& out) const {
  using ::apache::thrift::to_string;
  out << "StringType(";
  out << ")";
}


UUIDType::~UUIDType() throw() {
}

std::ostream& operator<<(std::ostream& out, const UUIDType& obj)
{
  obj.printTo(out);
  return out;
}


uint32_t UUIDType::read(::apache::thrift::protocol::TProtocol* iprot) {

  ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
  uint32_t xfer = 0;
  std::string fname;
  ::apache::thrift::protocol::TType ftype;
  int16_t fid;

  xfer += iprot->readStructBegin(fname);

  using ::apache::thrift::protocol::TProtocolException;


  while (true)
  {
    xfer += iprot->readFieldBegin(fname, ftype, fid);
    if (ftype == ::apache::thrift::protocol::T_STOP) {
      break;
    }
    xfer += iprot->skip(ftype);
    xfer += iprot->readFieldEnd();
  }

  xfer += iprot->readStructEnd();

  return xfer;
}

uint32_t UUIDType::write(::apache::thrift::protocol::TProtocol* oprot) const {
  uint32_t xfer = 0;
  ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
  xfer += oprot->writeStructBegin("UUIDType");

  xfer += oprot->writeFieldStop();
  xfer += oprot->writeStructEnd();
  return xfer;
}

void swap(UUIDType &a, UUIDType &b) {
  using ::std::swap;
  (void) a;
  (void) b;
}

UUIDType::UUIDType(const UUIDType& other4) {
  (void) other4;
}
UUIDType& UUIDType::operator=(const UUIDType& other5) {
  (void) other5;
  return *this;
}
void UUIDType::printTo(std::ostream& out) const {
  using ::apache::thrift::to_string;
  out << "UUIDType(";
  out << ")";
}


MapType::~MapType() throw() {
}

std::ostream& operator<<(std::ostream& out, const MapType& obj)
{
  obj.printTo(out);
  return out;
}


uint32_t MapType::read(::apache::thrift::protocol::TProtocol* iprot) {

  ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
  uint32_t xfer = 0;
  std::string fname;
  ::apache::thrift::protocol::TType ftype;
  int16_t fid;

  xfer += iprot->readStructBegin(fname);

  using ::apache::thrift::protocol::TProtocolException;


  while (true)
  {
    xfer += iprot->readFieldBegin(fname, ftype, fid);
    if (ftype == ::apache::thrift::protocol::T_STOP) {
      break;
    }
    xfer += iprot->skip(ftype);
    xfer += iprot->readFieldEnd();
  }

  xfer += iprot->readStructEnd();

  return xfer;
}

uint32_t MapType::write(::apache::thrift::protocol::TProtocol* oprot) const {
  uint32_t xfer = 0;
  ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
  xfer += oprot->writeStructBegin("MapType");

  xfer += oprot->writeFieldStop();
  xfer += oprot->writeStructEnd();
  return xfer;
}

void swap(MapType &a, MapType &b) {
  using ::std::swap;
  (void) a;
  (void) b;
}

MapType::MapType(const MapType& other6) {
  (void) other6;
}
MapType& MapType::operator=(const MapType& other7) {
  (void) other7;
  return *this;
}
void MapType::printTo(std::ostream& out) const {
  using ::apache::thrift::to_string;
  out << "MapType(";
  out << ")";
}


ListType::~ListType() throw() {
}

std::ostream& operator<<(std::ostream& out, const ListType& obj)
{
  obj.printTo(out);
  return out;
}


uint32_t ListType::read(::apache::thrift::protocol::TProtocol* iprot) {

  ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
  uint32_t xfer = 0;
  std::string fname;
  ::apache::thrift::protocol::TType ftype;
  int16_t fid;

  xfer += iprot->readStructBegin(fname);

  using ::apache::thrift::protocol::TProtocolException;


  while (true)
  {
    xfer += iprot->readFieldBegin(fname, ftype, fid);
    if (ftype == ::apache::thrift::protocol::T_STOP) {
      break;
    }
    xfer += iprot->skip(ftype);
    xfer += iprot->readFieldEnd();
  }

  xfer += iprot->readStructEnd();

  return xfer;
}

uint32_t ListType::write(::apache::thrift::protocol::TProtocol* oprot) const {
  uint32_t xfer = 0;
  ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
  xfer += oprot->writeStructBegin("ListType");

  xfer += oprot->writeFieldStop();
  xfer += oprot->writeStructEnd();
  return xfer;
}

void swap(ListType &a, ListType &b) {
  using ::std::swap;
  (void) a;
  (void) b;
}

ListType::ListType(const ListType& other8) {
  (void) other8;
}
ListType& ListType::operator=(const ListType& other9) {
  (void) other9;
  return *this;
}
void ListType::printTo(std::ostream& out) const {
  using ::apache::thrift::to_string;
  out << "ListType(";
  out << ")";
}


EnumType::~EnumType() throw() {
}

std::ostream& operator<<(std::ostream& out, const EnumType& obj)
{
  obj.printTo(out);
  return out;
}


uint32_t EnumType::read(::apache::thrift::protocol::TProtocol* iprot) {

  ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
  uint32_t xfer = 0;
  std::string fname;
  ::apache::thrift::protocol::TType ftype;
  int16_t fid;

  xfer += iprot->readStructBegin(fname);

  using ::apache::thrift::protocol::TProtocolException;


  while (true)
  {
    xfer += iprot->readFieldBegin(fname, ftype, fid);
    if (ftype == ::apache::thrift::protocol::T_STOP) {
      break;
    }
    xfer += iprot->skip(ftype);
    xfer += iprot->readFieldEnd();
  }

  xfer += iprot->readStructEnd();

  return xfer;
}

uint32_t EnumType::write(::apache::thrift::protocol::TProtocol* oprot) const {
  uint32_t xfer = 0;
  ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
  xfer += oprot->writeStructBegin("EnumType");

  xfer += oprot->writeFieldStop();
  xfer += oprot->writeStructEnd();
  return xfer;
}

void swap(EnumType &a, EnumType &b) {
  using ::std::swap;
  (void) a;
  (void) b;
}

EnumType::EnumType(const EnumType& other10) {
  (void) other10;
}
EnumType& EnumType::operator=(const EnumType& other11) {
  (void) other11;
  return *this;
}
void EnumType::printTo(std::ostream& out) const {
  using ::apache::thrift::to_string;
  out << "EnumType(";
  out << ")";
}


DateType::~DateType() throw() {
}

std::ostream& operator<<(std::ostream& out, const DateType& obj)
{
  obj.printTo(out);
  return out;
}


uint32_t DateType::read(::apache::thrift::protocol::TProtocol* iprot) {

  ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
  uint32_t xfer = 0;
  std::string fname;
  ::apache::thrift::protocol::TType ftype;
  int16_t fid;

  xfer += iprot->readStructBegin(fname);

  using ::apache::thrift::protocol::TProtocolException;


  while (true)
  {
    xfer += iprot->readFieldBegin(fname, ftype, fid);
    if (ftype == ::apache::thrift::protocol::T_STOP) {
      break;
    }
    xfer += iprot->skip(ftype);
    xfer += iprot->readFieldEnd();
  }

  xfer += iprot->readStructEnd();

  return xfer;
}

uint32_t DateType::write(::apache::thrift::protocol::TProtocol* oprot) const {
  uint32_t xfer = 0;
  ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
  xfer += oprot->writeStructBegin("DateType");

  xfer += oprot->writeFieldStop();
  xfer += oprot->writeStructEnd();
  return xfer;
}

void swap(DateType &a, DateType &b) {
  using ::std::swap;
  (void) a;
  (void) b;
}

DateType::DateType(const DateType& other12) {
  (void) other12;
}
DateType& DateType::operator=(const DateType& other13) {
  (void) other13;
  return *this;
}
void DateType::printTo(std::ostream& out) const {
  using ::apache::thrift::to_string;
  out << "DateType(";
  out << ")";
}


NullType::~NullType() throw() {
}

std::ostream& operator<<(std::ostream& out, const NullType& obj)
{
  obj.printTo(out);
  return out;
}


uint32_t NullType::read(::apache::thrift::protocol::TProtocol* iprot) {

  ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
  uint32_t xfer = 0;
  std::string fname;
  ::apache::thrift::protocol::TType ftype;
  int16_t fid;

  xfer += iprot->readStructBegin(fname);

  using ::apache::thrift::protocol::TProtocolException;


  while (true)
  {
    xfer += iprot->readFieldBegin(fname, ftype, fid);
    if (ftype == ::apache::thrift::protocol::T_STOP) {
      break;
    }
    xfer += iprot->skip(ftype);
    xfer += iprot->readFieldEnd();
  }

  xfer += iprot->readStructEnd();

  return xfer;
}

uint32_t NullType::write(::apache::thrift::protocol::TProtocol* oprot) const {
  uint32_t xfer = 0;
  ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
  xfer += oprot->writeStructBegin("NullType");

  xfer += oprot->writeFieldStop();
  xfer += oprot->writeStructEnd();
  return xfer;
}

void swap(NullType &a, NullType &b) {
  using ::std::swap;
  (void) a;
  (void) b;
}

NullType::NullType(const NullType& other14) {
  (void) other14;
}
NullType& NullType::operator=(const NullType& other15) {
  (void) other15;
  return *this;
}
void NullType::printTo(std::ostream& out) const {
  using ::apache::thrift::to_string;
  out << "NullType(";
  out << ")";
}


DecimalType::~DecimalType() throw() {
}


void DecimalType::__set_scale(const int32_t val) {
  this->scale = val;
}

void DecimalType::__set_precision(const int32_t val) {
  this->precision = val;
}
std::ostream& operator<<(std::ostream& out, const DecimalType& obj)
{
  obj.printTo(out);
  return out;
}


uint32_t DecimalType::read(::apache::thrift::protocol::TProtocol* iprot) {

  ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
  uint32_t xfer = 0;
  std::string fname;
  ::apache::thrift::protocol::TType ftype;
  int16_t fid;

  xfer += iprot->readStructBegin(fname);

  using ::apache::thrift::protocol::TProtocolException;

  bool isset_scale = false;
  bool isset_precision = false;

  while (true)
  {
    xfer += iprot->readFieldBegin(fname, ftype, fid);
    if (ftype == ::apache::thrift::protocol::T_STOP) {
      break;
    }
    switch (fid)
    {
      case 1:
        if (ftype == ::apache::thrift::protocol::T_I32) {
          xfer += iprot->readI32(this->scale);
          isset_scale = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 2:
        if (ftype == ::apache::thrift::protocol::T_I32) {
          xfer += iprot->readI32(this->precision);
          isset_precision = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      default:
        xfer += iprot->skip(ftype);
        break;
    }
    xfer += iprot->readFieldEnd();
  }

  xfer += iprot->readStructEnd();

  if (!isset_scale)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  if (!isset_precision)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  return xfer;
}

uint32_t DecimalType::write(::apache::thrift::protocol::TProtocol* oprot) const {
  uint32_t xfer = 0;
  ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
  xfer += oprot->writeStructBegin("DecimalType");

  xfer += oprot->writeFieldBegin("scale", ::apache::thrift::protocol::T_I32, 1);
  xfer += oprot->writeI32(this->scale);
  xfer += oprot->writeFieldEnd();

  xfer += oprot->writeFieldBegin("precision", ::apache::thrift::protocol::T_I32, 2);
  xfer += oprot->writeI32(this->precision);
  xfer += oprot->writeFieldEnd();

  xfer += oprot->writeFieldStop();
  xfer += oprot->writeStructEnd();
  return xfer;
}

void swap(DecimalType &a, DecimalType &b) {
  using ::std::swap;
  swap(a.scale, b.scale);
  swap(a.precision, b.precision);
}

DecimalType::DecimalType(const DecimalType& other16) {
  scale = other16.scale;
  precision = other16.precision;
}
DecimalType& DecimalType::operator=(const DecimalType& other17) {
  scale = other17.scale;
  precision = other17.precision;
  return *this;
}
void DecimalType::printTo(std::ostream& out) const {
  using ::apache::thrift::to_string;
  out << "DecimalType(";
  out << "scale=" << to_string(scale);
  out << ", " << "precision=" << to_string(precision);
  out << ")";
}


MilliSeconds::~MilliSeconds() throw() {
}

std::ostream& operator<<(std::ostream& out, const MilliSeconds& obj)
{
  obj.printTo(out);
  return out;
}


uint32_t MilliSeconds::read(::apache::thrift::protocol::TProtocol* iprot) {

  ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
  uint32_t xfer = 0;
  std::string fname;
  ::apache::thrift::protocol::TType ftype;
  int16_t fid;

  xfer += iprot->readStructBegin(fname);

  using ::apache::thrift::protocol::TProtocolException;


  while (true)
  {
    xfer += iprot->readFieldBegin(fname, ftype, fid);
    if (ftype == ::apache::thrift::protocol::T_STOP) {
      break;
    }
    xfer += iprot->skip(ftype);
    xfer += iprot->readFieldEnd();
  }

  xfer += iprot->readStructEnd();

  return xfer;
}

uint32_t MilliSeconds::write(::apache::thrift::protocol::TProtocol* oprot) const {
  uint32_t xfer = 0;
  ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
  xfer += oprot->writeStructBegin("MilliSeconds");

  xfer += oprot->writeFieldStop();
  xfer += oprot->writeStructEnd();
  return xfer;
}

void swap(MilliSeconds &a, MilliSeconds &b) {
  using ::std::swap;
  (void) a;
  (void) b;
}

MilliSeconds::MilliSeconds(const MilliSeconds& other18) {
  (void) other18;
}
MilliSeconds& MilliSeconds::operator=(const MilliSeconds& other19) {
  (void) other19;
  return *this;
}
void MilliSeconds::printTo(std::ostream& out) const {
  using ::apache::thrift::to_string;
  out << "MilliSeconds(";
  out << ")";
}


MicroSeconds::~MicroSeconds() throw() {
}

std::ostream& operator<<(std::ostream& out, const MicroSeconds& obj)
{
  obj.printTo(out);
  return out;
}


uint32_t MicroSeconds::read(::apache::thrift::protocol::TProtocol* iprot) {

  ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
  uint32_t xfer = 0;
  std::string fname;
  ::apache::thrift::protocol::TType ftype;
  int16_t fid;

  xfer += iprot->readStructBegin(fname);

  using ::apache::thrift::protocol::TProtocolException;


  while (true)
  {
    xfer += iprot->readFieldBegin(fname, ftype, fid);
    if (ftype == ::apache::thrift::protocol::T_STOP) {
      break;
    }
    xfer += iprot->skip(ftype);
    xfer += iprot->readFieldEnd();
  }

  xfer += iprot->readStructEnd();

  return xfer;
}

uint32_t MicroSeconds::write(::apache::thrift::protocol::TProtocol* oprot) const {
  uint32_t xfer = 0;
  ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
  xfer += oprot->writeStructBegin("MicroSeconds");

  xfer += oprot->writeFieldStop();
  xfer += oprot->writeStructEnd();
  return xfer;
}

void swap(MicroSeconds &a, MicroSeconds &b) {
  using ::std::swap;
  (void) a;
  (void) b;
}

MicroSeconds::MicroSeconds(const MicroSeconds& other20) {
  (void) other20;
}
MicroSeconds& MicroSeconds::operator=(const MicroSeconds& other21) {
  (void) other21;
  return *this;
}
void MicroSeconds::printTo(std::ostream& out) const {
  using ::apache::thrift::to_string;
  out << "MicroSeconds(";
  out << ")";
}


NanoSeconds::~NanoSeconds() throw() {
}

std::ostream& operator<<(std::ostream& out, const NanoSeconds& obj)
{
  obj.printTo(out);
  return out;
}


uint32_t NanoSeconds::read(::apache::thrift::protocol::TProtocol* iprot) {

  ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
  uint32_t xfer = 0;
  std::string fname;
  ::apache::thrift::protocol::TType ftype;
  int16_t fid;

  xfer += iprot->readStructBegin(fname);

  using ::apache::thrift::protocol::TProtocolException;


  while (true)
  {
    xfer += iprot->readFieldBegin(fname, ftype, fid);
    if (ftype == ::apache::thrift::protocol::T_STOP) {
      break;
    }
    xfer += iprot->skip(ftype);
    xfer += iprot->readFieldEnd();
  }

  xfer += iprot->readStructEnd();

  return xfer;
}

uint32_t NanoSeconds::write(::apache::thrift::protocol::TProtocol* oprot) const {
  uint32_t xfer = 0;
  ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
  xfer += oprot->writeStructBegin("NanoSeconds");

  xfer += oprot->writeFieldStop();
  xfer += oprot->writeStructEnd();
  return xfer;
}

void swap(NanoSeconds &a, NanoSeconds &b) {
  using ::std::swap;
  (void) a;
  (void) b;
}

NanoSeconds::NanoSeconds(const NanoSeconds& other22) {
  (void) other22;
}
NanoSeconds& NanoSeconds::operator=(const NanoSeconds& other23) {
  (void) other23;
  return *this;
}
void NanoSeconds::printTo(std::ostream& out) const {
  using ::apache::thrift::to_string;
  out << "NanoSeconds(";
  out << ")";
}


TimeUnit::~TimeUnit() throw() {
}


void TimeUnit::__set_MILLIS(const MilliSeconds& val) {
  this->MILLIS = val;
__isset.MILLIS = true;
}

void TimeUnit::__set_MICROS(const MicroSeconds& val) {
  this->MICROS = val;
__isset.MICROS = true;
}

void TimeUnit::__set_NANOS(const NanoSeconds& val) {
  this->NANOS = val;
__isset.NANOS = true;
}
std::ostream& operator<<(std::ostream& out, const TimeUnit& obj)
{
  obj.printTo(out);
  return out;
}


uint32_t TimeUnit::read(::apache::thrift::protocol::TProtocol* iprot) {

  ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
  uint32_t xfer = 0;
  std::string fname;
  ::apache::thrift::protocol::TType ftype;
  int16_t fid;

  xfer += iprot->readStructBegin(fname);

  using ::apache::thrift::protocol::TProtocolException;


  while (true)
  {
    xfer += iprot->readFieldBegin(fname, ftype, fid);
    if (ftype == ::apache::thrift::protocol::T_STOP) {
      break;
    }
    switch (fid)
    {
      case 1:
        if (ftype == ::apache::thrift::protocol::T_STRUCT) {
          xfer += this->MILLIS.read(iprot);
          this->__isset.MILLIS = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 2:
        if (ftype == ::apache::thrift::protocol::T_STRUCT) {
          xfer += this->MICROS.read(iprot);
          this->__isset.MICROS = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 3:
        if (ftype == ::apache::thrift::protocol::T_STRUCT) {
          xfer += this->NANOS.read(iprot);
          this->__isset.NANOS = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      default:
        xfer += iprot->skip(ftype);
        break;
    }
    xfer += iprot->readFieldEnd();
  }

  xfer += iprot->readStructEnd();

  return xfer;
}

uint32_t TimeUnit::write(::apache::thrift::protocol::TProtocol* oprot) const {
  uint32_t xfer = 0;
  ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
  xfer += oprot->writeStructBegin("TimeUnit");

  if (this->__isset.MILLIS) {
    xfer += oprot->writeFieldBegin("MILLIS", ::apache::thrift::protocol::T_STRUCT, 1);
    xfer += this->MILLIS.write(oprot);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.MICROS) {
    xfer += oprot->writeFieldBegin("MICROS", ::apache::thrift::protocol::T_STRUCT, 2);
    xfer += this->MICROS.write(oprot);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.NANOS) {
    xfer += oprot->writeFieldBegin("NANOS", ::apache::thrift::protocol::T_STRUCT, 3);
    xfer += this->NANOS.write(oprot);
    xfer += oprot->writeFieldEnd();
  }
  xfer += oprot->writeFieldStop();
  xfer += oprot->writeStructEnd();
  return xfer;
}

void swap(TimeUnit &a, TimeUnit &b) {
  using ::std::swap;
  swap(a.MILLIS, b.MILLIS);
  swap(a.MICROS, b.MICROS);
  swap(a.NANOS, b.NANOS);
  swap(a.__isset, b.__isset);
}

TimeUnit::TimeUnit(const TimeUnit& other24) {
  MILLIS = other24.MILLIS;
  MICROS = other24.MICROS;
  NANOS = other24.NANOS;
  __isset = other24.__isset;
}
TimeUnit& TimeUnit::operator=(const TimeUnit& other25) {
  MILLIS = other25.MILLIS;
  MICROS = other25.MICROS;
  NANOS = other25.NANOS;
  __isset = other25.__isset;
  return *this;
}
void TimeUnit::printTo(std::ostream& out) const {
  using ::apache::thrift::to_string;
  out << "TimeUnit(";
  out << "MILLIS="; (__isset.MILLIS ? (out << to_string(MILLIS)) : (out << "<null>"));
  out << ", " << "MICROS="; (__isset.MICROS ? (out << to_string(MICROS)) : (out << "<null>"));
  out << ", " << "NANOS="; (__isset.NANOS ? (out << to_string(NANOS)) : (out << "<null>"));
  out << ")";
}


TimestampType::~TimestampType() throw() {
}


void TimestampType::__set_isAdjustedToUTC(const bool val) {
  this->isAdjustedToUTC = val;
}

void TimestampType::__set_unit(const TimeUnit& val) {
  this->unit = val;
}
std::ostream& operator<<(std::ostream& out, const TimestampType& obj)
{
  obj.printTo(out);
  return out;
}


uint32_t TimestampType::read(::apache::thrift::protocol::TProtocol* iprot) {

  ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
  uint32_t xfer = 0;
  std::string fname;
  ::apache::thrift::protocol::TType ftype;
  int16_t fid;

  xfer += iprot->readStructBegin(fname);

  using ::apache::thrift::protocol::TProtocolException;

  bool isset_isAdjustedToUTC = false;
  bool isset_unit = false;

  while (true)
  {
    xfer += iprot->readFieldBegin(fname, ftype, fid);
    if (ftype == ::apache::thrift::protocol::T_STOP) {
      break;
    }
    switch (fid)
    {
      case 1:
        if (ftype == ::apache::thrift::protocol::T_BOOL) {
          xfer += iprot->readBool(this->isAdjustedToUTC);
          isset_isAdjustedToUTC = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 2:
        if (ftype == ::apache::thrift::protocol::T_STRUCT) {
          xfer += this->unit.read(iprot);
          isset_unit = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      default:
        xfer += iprot->skip(ftype);
        break;
    }
    xfer += iprot->readFieldEnd();
  }

  xfer += iprot->readStructEnd();

  if (!isset_isAdjustedToUTC)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  if (!isset_unit)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  return xfer;
}

uint32_t TimestampType::write(::apache::thrift::protocol::TProtocol* oprot) const {
  uint32_t xfer = 0;
  ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
  xfer += oprot->writeStructBegin("TimestampType");

  xfer += oprot->writeFieldBegin("isAdjustedToUTC", ::apache::thrift::protocol::T_BOOL, 1);
  xfer += oprot->writeBool(this->isAdjustedToUTC);
  xfer += oprot->writeFieldEnd();

  xfer += oprot->writeFieldBegin("unit", ::apache::thrift::protocol::T_STRUCT, 2);
  xfer += this->unit.write(oprot);
  xfer += oprot->writeFieldEnd();

  xfer += oprot->writeFieldStop();
  xfer += oprot->writeStructEnd();
  return xfer;
}

void swap(TimestampType &a, TimestampType &b) {
  using ::std::swap;
  swap(a.isAdjustedToUTC, b.isAdjustedToUTC);
  swap(a.unit, b.unit);
}

TimestampType::TimestampType(const TimestampType& other26) {
  isAdjustedToUTC = other26.isAdjustedToUTC;
  unit = other26.unit;
}
TimestampType& TimestampType::operator=(const TimestampType& other27) {
  isAdjustedToUTC = other27.isAdjustedToUTC;
  unit = other27.unit;
  return *this;
}
void TimestampType::printTo(std::ostream& out) const {
  using ::apache::thrift::to_string;
  out << "TimestampType(";
  out << "isAdjustedToUTC=" << to_string(isAdjustedToUTC);
  out << ", " << "unit=" << to_string(unit);
  out << ")";
}


TimeType::~TimeType() throw() {
}


void TimeType::__set_isAdjustedToUTC(const bool val) {
  this->isAdjustedToUTC = val;
}

void TimeType::__set_unit(const TimeUnit& val) {
  this->unit = val;
}
std::ostream& operator<<(std::ostream& out, const TimeType& obj)
{
  obj.printTo(out);
  return out;
}


uint32_t TimeType::read(::apache::thrift::protocol::TProtocol* iprot) {

  ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
  uint32_t xfer = 0;
  std::string fname;
  ::apache::thrift::protocol::TType ftype;
  int16_t fid;

  xfer += iprot->readStructBegin(fname);

  using ::apache::thrift::protocol::TProtocolException;

  bool isset_isAdjustedToUTC = false;
  bool isset_unit = false;

  while (true)
  {
    xfer += iprot->readFieldBegin(fname, ftype, fid);
    if (ftype == ::apache::thrift::protocol::T_STOP) {
      break;
    }
    switch (fid)
    {
      case 1:
        if (ftype == ::apache::thrift::protocol::T_BOOL) {
          xfer += iprot->readBool(this->isAdjustedToUTC);
          isset_isAdjustedToUTC = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 2:
        if (ftype == ::apache::thrift::protocol::T_STRUCT) {
          xfer += this->unit.read(iprot);
          isset_unit = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      default:
        xfer += iprot->skip(ftype);
        break;
    }
    xfer += iprot->readFieldEnd();
  }

  xfer += iprot->readStructEnd();

  if (!isset_isAdjustedToUTC)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  if (!isset_unit)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  return xfer;
}

uint32_t TimeType::write(::apache::thrift::protocol::TProtocol* oprot) const {
  uint32_t xfer = 0;
  ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
  xfer += oprot->writeStructBegin("TimeType");

  xfer += oprot->writeFieldBegin("isAdjustedToUTC", ::apache::thrift::protocol::T_BOOL, 1);
  xfer += oprot->writeBool(this->isAdjustedToUTC);
  xfer += oprot->writeFieldEnd();

  xfer += oprot->writeFieldBegin("unit", ::apache::thrift::protocol::T_STRUCT, 2);
  xfer += this->unit.write(oprot);
  xfer += oprot->writeFieldEnd();

  xfer += oprot->writeFieldStop();
  xfer += oprot->writeStructEnd();
  return xfer;
}

void swap(TimeType &a, TimeType &b) {
  using ::std::swap;
  swap(a.isAdjustedToUTC, b.isAdjustedToUTC);
  swap(a.unit, b.unit);
}

TimeType::TimeType(const TimeType& other28) {
  isAdjustedToUTC = other28.isAdjustedToUTC;
  unit = other28.unit;
}
TimeType& TimeType::operator=(const TimeType& other29) {
  isAdjustedToUTC = other29.isAdjustedToUTC;
  unit = other29.unit;
  return *this;
}
void TimeType::printTo(std::ostream& out) const {
  using ::apache::thrift::to_string;
  out << "TimeType(";
  out << "isAdjustedToUTC=" << to_string(isAdjustedToUTC);
  out << ", " << "unit=" << to_string(unit);
  out << ")";
}


IntType::~IntType() throw() {
}


void IntType::__set_bitWidth(const int8_t val) {
  this->bitWidth = val;
}

void IntType::__set_isSigned(const bool val) {
  this->isSigned = val;
}
std::ostream& operator<<(std::ostream& out, const IntType& obj)
{
  obj.printTo(out);
  return out;
}


uint32_t IntType::read(::apache::thrift::protocol::TProtocol* iprot) {

  ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
  uint32_t xfer = 0;
  std::string fname;
  ::apache::thrift::protocol::TType ftype;
  int16_t fid;

  xfer += iprot->readStructBegin(fname);

  using ::apache::thrift::protocol::TProtocolException;

  bool isset_bitWidth = false;
  bool isset_isSigned = false;

  while (true)
  {
    xfer += iprot->readFieldBegin(fname, ftype, fid);
    if (ftype == ::apache::thrift::protocol::T_STOP) {
      break;
    }
    switch (fid)
    {
      case 1:
        if (ftype == ::apache::thrift::protocol::T_BYTE) {
          xfer += iprot->readByte(this->bitWidth);
          isset_bitWidth = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 2:
        if (ftype == ::apache::thrift::protocol::T_BOOL) {
          xfer += iprot->readBool(this->isSigned);
          isset_isSigned = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      default:
        xfer += iprot->skip(ftype);
        break;
    }
    xfer += iprot->readFieldEnd();
  }

  xfer += iprot->readStructEnd();

  if (!isset_bitWidth)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  if (!isset_isSigned)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  return xfer;
}

uint32_t IntType::write(::apache::thrift::protocol::TProtocol* oprot) const {
  uint32_t xfer = 0;
  ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
  xfer += oprot->writeStructBegin("IntType");

  xfer += oprot->writeFieldBegin("bitWidth", ::apache::thrift::protocol::T_BYTE, 1);
  xfer += oprot->writeByte(this->bitWidth);
  xfer += oprot->writeFieldEnd();

  xfer += oprot->writeFieldBegin("isSigned", ::apache::thrift::protocol::T_BOOL, 2);
  xfer += oprot->writeBool(this->isSigned);
  xfer += oprot->writeFieldEnd();

  xfer += oprot->writeFieldStop();
  xfer += oprot->writeStructEnd();
  return xfer;
}

void swap(IntType &a, IntType &b) {
  using ::std::swap;
  swap(a.bitWidth, b.bitWidth);
  swap(a.isSigned, b.isSigned);
}

IntType::IntType(const IntType& other30) {
  bitWidth = other30.bitWidth;
  isSigned = other30.isSigned;
}
IntType& IntType::operator=(const IntType& other31) {
  bitWidth = other31.bitWidth;
  isSigned = other31.isSigned;
  return *this;
}
void IntType::printTo(std::ostream& out) const {
  using ::apache::thrift::to_string;
  out << "IntType(";
  out << "bitWidth=" << to_string(bitWidth);
  out << ", " << "isSigned=" << to_string(isSigned);
  out << ")";
}


JsonType::~JsonType() throw() {
}

std::ostream& operator<<(std::ostream& out, const JsonType& obj)
{
  obj.printTo(out);
  return out;
}


uint32_t JsonType::read(::apache::thrift::protocol::TProtocol* iprot) {

  ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
  uint32_t xfer = 0;
  std::string fname;
  ::apache::thrift::protocol::TType ftype;
  int16_t fid;

  xfer += iprot->readStructBegin(fname);

  using ::apache::thrift::protocol::TProtocolException;


  while (true)
  {
    xfer += iprot->readFieldBegin(fname, ftype, fid);
    if (ftype == ::apache::thrift::protocol::T_STOP) {
      break;
    }
    xfer += iprot->skip(ftype);
    xfer += iprot->readFieldEnd();
  }

  xfer += iprot->readStructEnd();

  return xfer;
}

uint32_t JsonType::write(::apache::thrift::protocol::TProtocol* oprot) const {
  uint32_t xfer = 0;
  ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
  xfer += oprot->writeStructBegin("JsonType");

  xfer += oprot->writeFieldStop();
  xfer += oprot->writeStructEnd();
  return xfer;
}

void swap(JsonType &a, JsonType &b) {
  using ::std::swap;
  (void) a;
  (void) b;
}

JsonType::JsonType(const JsonType& other32) {
  (void) other32;
}
JsonType& JsonType::operator=(const JsonType& other33) {
  (void) other33;
  return *this;
}
void JsonType::printTo(std::ostream& out) const {
  using ::apache::thrift::to_string;
  out << "JsonType(";
  out << ")";
}


BsonType::~BsonType() throw() {
}

std::ostream& operator<<(std::ostream& out, const BsonType& obj)
{
  obj.printTo(out);
  return out;
}


uint32_t BsonType::read(::apache::thrift::protocol::TProtocol* iprot) {

  ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
  uint32_t xfer = 0;
  std::string fname;
  ::apache::thrift::protocol::TType ftype;
  int16_t fid;

  xfer += iprot->readStructBegin(fname);

  using ::apache::thrift::protocol::TProtocolException;


  while (true)
  {
    xfer += iprot->readFieldBegin(fname, ftype, fid);
    if (ftype == ::apache::thrift::protocol::T_STOP) {
      break;
    }
    xfer += iprot->skip(ftype);
    xfer += iprot->readFieldEnd();
  }

  xfer += iprot->readStructEnd();

  return xfer;
}

uint32_t BsonType::write(::apache::thrift::protocol::TProtocol* oprot) const {
  uint32_t xfer = 0;
  ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
  xfer += oprot->writeStructBegin("BsonType");

  xfer += oprot->writeFieldStop();
  xfer += oprot->writeStructEnd();
  return xfer;
}

void swap(BsonType &a, BsonType &b) {
  using ::std::swap;
  (void) a;
  (void) b;
}

BsonType::BsonType(const BsonType& other34) {
  (void) other34;
}
BsonType& BsonType::operator=(const BsonType& other35) {
  (void) other35;
  return *this;
}
void BsonType::printTo(std::ostream& out) const {
  using ::apache::thrift::to_string;
  out << "BsonType(";
  out << ")";
}


LogicalType::~LogicalType() throw() {
}


void LogicalType::__set_STRING(const StringType& val) {
  this->STRING = val;
__isset.STRING = true;
}

void LogicalType::__set_MAP(const MapType& val) {
  this->MAP = val;
__isset.MAP = true;
}

void LogicalType::__set_LIST(const ListType& val) {
  this->LIST = val;
__isset.LIST = true;
}

void LogicalType::__set_ENUM(const EnumType& val) {
  this->ENUM = val;
__isset.ENUM = true;
}

void LogicalType::__set_DECIMAL(const DecimalType& val) {
  this->DECIMAL = val;
__isset.DECIMAL = true;
}

void LogicalType::__set_DATE(const DateType& val) {
  this->DATE = val;
__isset.DATE = true;
}

void LogicalType::__set_TIME(const TimeType& val) {
  this->TIME = val;
__isset.TIME = true;
}

void LogicalType::__set_TIMESTAMP(const TimestampType& val) {
  this->TIMESTAMP = val;
__isset.TIMESTAMP = true;
}

void LogicalType::__set_INTEGER(const IntType& val) {
  this->INTEGER = val;
__isset.INTEGER = true;
}

void LogicalType::__set_UNKNOWN(const NullType& val) {
  this->UNKNOWN = val;
__isset.UNKNOWN = true;
}

void LogicalType::__set_JSON(const JsonType& val) {
  this->JSON = val;
__isset.JSON = true;
}

void LogicalType::__set_BSON(const BsonType& val) {
  this->BSON = val;
__isset.BSON = true;
}

void LogicalType::__set_UUID(const UUIDType& val) {
  this->UUID = val;
__isset.UUID = true;
}
std::ostream& operator<<(std::ostream& out, const LogicalType& obj)
{
  obj.printTo(out);
  return out;
}


uint32_t LogicalType::read(::apache::thrift::protocol::TProtocol* iprot) {

  ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
  uint32_t xfer = 0;
  std::string fname;
  ::apache::thrift::protocol::TType ftype;
  int16_t fid;

  xfer += iprot->readStructBegin(fname);

  using ::apache::thrift::protocol::TProtocolException;


  while (true)
  {
    xfer += iprot->readFieldBegin(fname, ftype, fid);
    if (ftype == ::apache::thrift::protocol::T_STOP) {
      break;
    }
    switch (fid)
    {
      case 1:
        if (ftype == ::apache::thrift::protocol::T_STRUCT) {
          xfer += this->STRING.read(iprot);
          this->__isset.STRING = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 2:
        if (ftype == ::apache::thrift::protocol::T_STRUCT) {
          xfer += this->MAP.read(iprot);
          this->__isset.MAP = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 3:
        if (ftype == ::apache::thrift::protocol::T_STRUCT) {
          xfer += this->LIST.read(iprot);
          this->__isset.LIST = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 4:
        if (ftype == ::apache::thrift::protocol::T_STRUCT) {
          xfer += this->ENUM.read(iprot);
          this->__isset.ENUM = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 5:
        if (ftype == ::apache::thrift::protocol::T_STRUCT) {
          xfer += this->DECIMAL.read(iprot);
          this->__isset.DECIMAL = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 6:
        if (ftype == ::apache::thrift::protocol::T_STRUCT) {
          xfer += this->DATE.read(iprot);
          this->__isset.DATE = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 7:
        if (ftype == ::apache::thrift::protocol::T_STRUCT) {
          xfer += this->TIME.read(iprot);
          this->__isset.TIME = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 8:
        if (ftype == ::apache::thrift::protocol::T_STRUCT) {
          xfer += this->TIMESTAMP.read(iprot);
          this->__isset.TIMESTAMP = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 10:
        if (ftype == ::apache::thrift::protocol::T_STRUCT) {
          xfer += this->INTEGER.read(iprot);
          this->__isset.INTEGER = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 11:
        if (ftype == ::apache::thrift::protocol::T_STRUCT) {
          xfer += this->UNKNOWN.read(iprot);
          this->__isset.UNKNOWN = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 12:
        if (ftype == ::apache::thrift::protocol::T_STRUCT) {
          xfer += this->JSON.read(iprot);
          this->__isset.JSON = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 13:
        if (ftype == ::apache::thrift::protocol::T_STRUCT) {
          xfer += this->BSON.read(iprot);
          this->__isset.BSON = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 14:
        if (ftype == ::apache::thrift::protocol::T_STRUCT) {
          xfer += this->UUID.read(iprot);
          this->__isset.UUID = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      default:
        xfer += iprot->skip(ftype);
        break;
    }
    xfer += iprot->readFieldEnd();
  }

  xfer += iprot->readStructEnd();

  return xfer;
}

uint32_t LogicalType::write(::apache::thrift::protocol::TProtocol* oprot) const {
  uint32_t xfer = 0;
  ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
  xfer += oprot->writeStructBegin("LogicalType");

  if (this->__isset.STRING) {
    xfer += oprot->writeFieldBegin("STRING", ::apache::thrift::protocol::T_STRUCT, 1);
    xfer += this->STRING.write(oprot);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.MAP) {
    xfer += oprot->writeFieldBegin("MAP", ::apache::thrift::protocol::T_STRUCT, 2);
    xfer += this->MAP.write(oprot);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.LIST) {
    xfer += oprot->writeFieldBegin("LIST", ::apache::thrift::protocol::T_STRUCT, 3);
    xfer += this->LIST.write(oprot);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.ENUM) {
    xfer += oprot->writeFieldBegin("ENUM", ::apache::thrift::protocol::T_STRUCT, 4);
    xfer += this->ENUM.write(oprot);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.DECIMAL) {
    xfer += oprot->writeFieldBegin("DECIMAL", ::apache::thrift::protocol::T_STRUCT, 5);
    xfer += this->DECIMAL.write(oprot);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.DATE) {
    xfer += oprot->writeFieldBegin("DATE", ::apache::thrift::protocol::T_STRUCT, 6);
    xfer += this->DATE.write(oprot);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.TIME) {
    xfer += oprot->writeFieldBegin("TIME", ::apache::thrift::protocol::T_STRUCT, 7);
    xfer += this->TIME.write(oprot);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.TIMESTAMP) {
    xfer += oprot->writeFieldBegin("TIMESTAMP", ::apache::thrift::protocol::T_STRUCT, 8);
    xfer += this->TIMESTAMP.write(oprot);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.INTEGER) {
    xfer += oprot->writeFieldBegin("INTEGER", ::apache::thrift::protocol::T_STRUCT, 10);
    xfer += this->INTEGER.write(oprot);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.UNKNOWN) {
    xfer += oprot->writeFieldBegin("UNKNOWN", ::apache::thrift::protocol::T_STRUCT, 11);
    xfer += this->UNKNOWN.write(oprot);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.JSON) {
    xfer += oprot->writeFieldBegin("JSON", ::apache::thrift::protocol::T_STRUCT, 12);
    xfer += this->JSON.write(oprot);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.BSON) {
    xfer += oprot->writeFieldBegin("BSON", ::apache::thrift::protocol::T_STRUCT, 13);
    xfer += this->BSON.write(oprot);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.UUID) {
    xfer += oprot->writeFieldBegin("UUID", ::apache::thrift::protocol::T_STRUCT, 14);
    xfer += this->UUID.write(oprot);
    xfer += oprot->writeFieldEnd();
  }
  xfer += oprot->writeFieldStop();
  xfer += oprot->writeStructEnd();
  return xfer;
}

void swap(LogicalType &a, LogicalType &b) {
  using ::std::swap;
  swap(a.STRING, b.STRING);
  swap(a.MAP, b.MAP);
  swap(a.LIST, b.LIST);
  swap(a.ENUM, b.ENUM);
  swap(a.DECIMAL, b.DECIMAL);
  swap(a.DATE, b.DATE);
  swap(a.TIME, b.TIME);
  swap(a.TIMESTAMP, b.TIMESTAMP);
  swap(a.INTEGER, b.INTEGER);
  swap(a.UNKNOWN, b.UNKNOWN);
  swap(a.JSON, b.JSON);
  swap(a.BSON, b.BSON);
  swap(a.UUID, b.UUID);
  swap(a.__isset, b.__isset);
}

LogicalType::LogicalType(const LogicalType& other36) {
  STRING = other36.STRING;
  MAP = other36.MAP;
  LIST = other36.LIST;
  ENUM = other36.ENUM;
  DECIMAL = other36.DECIMAL;
  DATE = other36.DATE;
  TIME = other36.TIME;
  TIMESTAMP = other36.TIMESTAMP;
  INTEGER = other36.INTEGER;
  UNKNOWN = other36.UNKNOWN;
  JSON = other36.JSON;
  BSON = other36.BSON;
  UUID = other36.UUID;
  __isset = other36.__isset;
}
LogicalType& LogicalType::operator=(const LogicalType& other37) {
  STRING = other37.STRING;
  MAP = other37.MAP;
  LIST = other37.LIST;
  ENUM = other37.ENUM;
  DECIMAL = other37.DECIMAL;
  DATE = other37.DATE;
  TIME = other37.TIME;
  TIMESTAMP = other37.TIMESTAMP;
  INTEGER = other37.INTEGER;
  UNKNOWN = other37.UNKNOWN;
  JSON = other37.JSON;
  BSON = other37.BSON;
  UUID = other37.UUID;
  __isset = other37.__isset;
  return *this;
}
void LogicalType::printTo(std::ostream& out) const {
  using ::apache::thrift::to_string;
  out << "LogicalType(";
  out << "STRING="; (__isset.STRING ? (out << to_string(STRING)) : (out << "<null>"));
  out << ", " << "MAP="; (__isset.MAP ? (out << to_string(MAP)) : (out << "<null>"));
  out << ", " << "LIST="; (__isset.LIST ? (out << to_string(LIST)) : (out << "<null>"));
  out << ", " << "ENUM="; (__isset.ENUM ? (out << to_string(ENUM)) : (out << "<null>"));
  out << ", " << "DECIMAL="; (__isset.DECIMAL ? (out << to_string(DECIMAL)) : (out << "<null>"));
  out << ", " << "DATE="; (__isset.DATE ? (out << to_string(DATE)) : (out << "<null>"));
  out << ", " << "TIME="; (__isset.TIME ? (out << to_string(TIME)) : (out << "<null>"));
  out << ", " << "TIMESTAMP="; (__isset.TIMESTAMP ? (out << to_string(TIMESTAMP)) : (out << "<null>"));
  out << ", " << "INTEGER="; (__isset.INTEGER ? (out << to_string(INTEGER)) : (out << "<null>"));
  out << ", " << "UNKNOWN="; (__isset.UNKNOWN ? (out << to_string(UNKNOWN)) : (out << "<null>"));
  out << ", " << "JSON="; (__isset.JSON ? (out << to_string(JSON)) : (out << "<null>"));
  out << ", " << "BSON="; (__isset.BSON ? (out << to_string(BSON)) : (out << "<null>"));
  out << ", " << "UUID="; (__isset.UUID ? (out << to_string(UUID)) : (out << "<null>"));
  out << ")";
}


SchemaElement::~SchemaElement() throw() {
}


void SchemaElement::__set_type(const Type::type val) {
  this->type = val;
__isset.type = true;
}

void SchemaElement::__set_type_length(const int32_t val) {
  this->type_length = val;
__isset.type_length = true;
}

void SchemaElement::__set_repetition_type(const FieldRepetitionType::type val) {
  this->repetition_type = val;
__isset.repetition_type = true;
}

void SchemaElement::__set_name(const std::string& val) {
  this->name = val;
}

void SchemaElement::__set_num_children(const int32_t val) {
  this->num_children = val;
__isset.num_children = true;
}

void SchemaElement::__set_converted_type(const ConvertedType::type val) {
  this->converted_type = val;
__isset.converted_type = true;
}

void SchemaElement::__set_scale(const int32_t val) {
  this->scale = val;
__isset.scale = true;
}

void SchemaElement::__set_precision(const int32_t val) {
  this->precision = val;
__isset.precision = true;
}

void SchemaElement::__set_field_id(const int32_t val) {
  this->field_id = val;
__isset.field_id = true;
}

void SchemaElement::__set_logicalType(const LogicalType& val) {
  this->logicalType = val;
__isset.logicalType = true;
}
std::ostream& operator<<(std::ostream& out, const SchemaElement& obj)
{
  obj.printTo(out);
  return out;
}


uint32_t SchemaElement::read(::apache::thrift::protocol::TProtocol* iprot) {

  ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
  uint32_t xfer = 0;
  std::string fname;
  ::apache::thrift::protocol::TType ftype;
  int16_t fid;

  xfer += iprot->readStructBegin(fname);

  using ::apache::thrift::protocol::TProtocolException;

  bool isset_name = false;

  while (true)
  {
    xfer += iprot->readFieldBegin(fname, ftype, fid);
    if (ftype == ::apache::thrift::protocol::T_STOP) {
      break;
    }
    switch (fid)
    {
      case 1:
        if (ftype == ::apache::thrift::protocol::T_I32) {
          int32_t ecast38;
          xfer += iprot->readI32(ecast38);
          this->type = (Type::type)ecast38;
          this->__isset.type = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 2:
        if (ftype == ::apache::thrift::protocol::T_I32) {
          xfer += iprot->readI32(this->type_length);
          this->__isset.type_length = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 3:
        if (ftype == ::apache::thrift::protocol::T_I32) {
          int32_t ecast39;
          xfer += iprot->readI32(ecast39);
          this->repetition_type = (FieldRepetitionType::type)ecast39;
          this->__isset.repetition_type = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 4:
        if (ftype == ::apache::thrift::protocol::T_STRING) {
          xfer += iprot->readString(this->name);
          isset_name = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 5:
        if (ftype == ::apache::thrift::protocol::T_I32) {
          xfer += iprot->readI32(this->num_children);
          this->__isset.num_children = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 6:
        if (ftype == ::apache::thrift::protocol::T_I32) {
          int32_t ecast40;
          xfer += iprot->readI32(ecast40);
          this->converted_type = (ConvertedType::type)ecast40;
          this->__isset.converted_type = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 7:
        if (ftype == ::apache::thrift::protocol::T_I32) {
          xfer += iprot->readI32(this->scale);
          this->__isset.scale = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 8:
        if (ftype == ::apache::thrift::protocol::T_I32) {
          xfer += iprot->readI32(this->precision);
          this->__isset.precision = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 9:
        if (ftype == ::apache::thrift::protocol::T_I32) {
          xfer += iprot->readI32(this->field_id);
          this->__isset.field_id = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 10:
        if (ftype == ::apache::thrift::protocol::T_STRUCT) {
          xfer += this->logicalType.read(iprot);
          this->__isset.logicalType = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      default:
        xfer += iprot->skip(ftype);
        break;
    }
    xfer += iprot->readFieldEnd();
  }

  xfer += iprot->readStructEnd();

  if (!isset_name)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  return xfer;
}

uint32_t SchemaElement::write(::apache::thrift::protocol::TProtocol* oprot) const {
  uint32_t xfer = 0;
  ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
  xfer += oprot->writeStructBegin("SchemaElement");

  if (this->__isset.type) {
    xfer += oprot->writeFieldBegin("type", ::apache::thrift::protocol::T_I32, 1);
    xfer += oprot->writeI32((int32_t)this->type);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.type_length) {
    xfer += oprot->writeFieldBegin("type_length", ::apache::thrift::protocol::T_I32, 2);
    xfer += oprot->writeI32(this->type_length);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.repetition_type) {
    xfer += oprot->writeFieldBegin("repetition_type", ::apache::thrift::protocol::T_I32, 3);
    xfer += oprot->writeI32((int32_t)this->repetition_type);
    xfer += oprot->writeFieldEnd();
  }
  xfer += oprot->writeFieldBegin("name", ::apache::thrift::protocol::T_STRING, 4);
  xfer += oprot->writeString(this->name);
  xfer += oprot->writeFieldEnd();

  if (this->__isset.num_children) {
    xfer += oprot->writeFieldBegin("num_children", ::apache::thrift::protocol::T_I32, 5);
    xfer += oprot->writeI32(this->num_children);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.converted_type) {
    xfer += oprot->writeFieldBegin("converted_type", ::apache::thrift::protocol::T_I32, 6);
    xfer += oprot->writeI32((int32_t)this->converted_type);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.scale) {
    xfer += oprot->writeFieldBegin("scale", ::apache::thrift::protocol::T_I32, 7);
    xfer += oprot->writeI32(this->scale);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.precision) {
    xfer += oprot->writeFieldBegin("precision", ::apache::thrift::protocol::T_I32, 8);
    xfer += oprot->writeI32(this->precision);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.field_id) {
    xfer += oprot->writeFieldBegin("field_id", ::apache::thrift::protocol::T_I32, 9);
    xfer += oprot->writeI32(this->field_id);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.logicalType) {
    xfer += oprot->writeFieldBegin("logicalType", ::apache::thrift::protocol::T_STRUCT, 10);
    xfer += this->logicalType.write(oprot);
    xfer += oprot->writeFieldEnd();
  }
  xfer += oprot->writeFieldStop();
  xfer += oprot->writeStructEnd();
  return xfer;
}

void swap(SchemaElement &a, SchemaElement &b) {
  using ::std::swap;
  swap(a.type, b.type);
  swap(a.type_length, b.type_length);
  swap(a.repetition_type, b.repetition_type);
  swap(a.name, b.name);
  swap(a.num_children, b.num_children);
  swap(a.converted_type, b.converted_type);
  swap(a.scale, b.scale);
  swap(a.precision, b.precision);
  swap(a.field_id, b.field_id);
  swap(a.logicalType, b.logicalType);
  swap(a.__isset, b.__isset);
}

SchemaElement::SchemaElement(const SchemaElement& other41) {
  type = other41.type;
  type_length = other41.type_length;
  repetition_type = other41.repetition_type;
  name = other41.name;
  num_children = other41.num_children;
  converted_type = other41.converted_type;
  scale = other41.scale;
  precision = other41.precision;
  field_id = other41.field_id;
  logicalType = other41.logicalType;
  __isset = other41.__isset;
}
SchemaElement& SchemaElement::operator=(const SchemaElement& other42) {
  type = other42.type;
  type_length = other42.type_length;
  repetition_type = other42.repetition_type;
  name = other42.name;
  num_children = other42.num_children;
  converted_type = other42.converted_type;
  scale = other42.scale;
  precision = other42.precision;
  field_id = other42.field_id;
  logicalType = other42.logicalType;
  __isset = other42.__isset;
  return *this;
}
void SchemaElement::printTo(std::ostream& out) const {
  using ::apache::thrift::to_string;
  out << "SchemaElement(";
  out << "type="; (__isset.type ? (out << to_string(type)) : (out << "<null>"));
  out << ", " << "type_length="; (__isset.type_length ? (out << to_string(type_length)) : (out << "<null>"));
  out << ", " << "repetition_type="; (__isset.repetition_type ? (out << to_string(repetition_type)) : (out << "<null>"));
  out << ", " << "name=" << to_string(name);
  out << ", " << "num_children="; (__isset.num_children ? (out << to_string(num_children)) : (out << "<null>"));
  out << ", " << "converted_type="; (__isset.converted_type ? (out << to_string(converted_type)) : (out << "<null>"));
  out << ", " << "scale="; (__isset.scale ? (out << to_string(scale)) : (out << "<null>"));
  out << ", " << "precision="; (__isset.precision ? (out << to_string(precision)) : (out << "<null>"));
  out << ", " << "field_id="; (__isset.field_id ? (out << to_string(field_id)) : (out << "<null>"));
  out << ", " << "logicalType="; (__isset.logicalType ? (out << to_string(logicalType)) : (out << "<null>"));
  out << ")";
}


DataPageHeader::~DataPageHeader() throw() {
}


void DataPageHeader::__set_num_values(const int32_t val) {
  this->num_values = val;
}

void DataPageHeader::__set_encoding(const Encoding::type val) {
  this->encoding = val;
}

void DataPageHeader::__set_definition_level_encoding(const Encoding::type val) {
  this->definition_level_encoding = val;
}

void DataPageHeader::__set_repetition_level_encoding(const Encoding::type val) {
  this->repetition_level_encoding = val;
}

void DataPageHeader::__set_statistics(const Statistics& val) {
  this->statistics = val;
__isset.statistics = true;
}
std::ostream& operator<<(std::ostream& out, const DataPageHeader& obj)
{
  obj.printTo(out);
  return out;
}


uint32_t DataPageHeader::read(::apache::thrift::protocol::TProtocol* iprot) {

  ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
  uint32_t xfer = 0;
  std::string fname;
  ::apache::thrift::protocol::TType ftype;
  int16_t fid;

  xfer += iprot->readStructBegin(fname);

  using ::apache::thrift::protocol::TProtocolException;

  bool isset_num_values = false;
  bool isset_encoding = false;
  bool isset_definition_level_encoding = false;
  bool isset_repetition_level_encoding = false;

  while (true)
  {
    xfer += iprot->readFieldBegin(fname, ftype, fid);
    if (ftype == ::apache::thrift::protocol::T_STOP) {
      break;
    }
    switch (fid)
    {
      case 1:
        if (ftype == ::apache::thrift::protocol::T_I32) {
          xfer += iprot->readI32(this->num_values);
          isset_num_values = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 2:
        if (ftype == ::apache::thrift::protocol::T_I32) {
          int32_t ecast43;
          xfer += iprot->readI32(ecast43);
          this->encoding = (Encoding::type)ecast43;
          isset_encoding = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 3:
        if (ftype == ::apache::thrift::protocol::T_I32) {
          int32_t ecast44;
          xfer += iprot->readI32(ecast44);
          this->definition_level_encoding = (Encoding::type)ecast44;
          isset_definition_level_encoding = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 4:
        if (ftype == ::apache::thrift::protocol::T_I32) {
          int32_t ecast45;
          xfer += iprot->readI32(ecast45);
          this->repetition_level_encoding = (Encoding::type)ecast45;
          isset_repetition_level_encoding = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 5:
        if (ftype == ::apache::thrift::protocol::T_STRUCT) {
          xfer += this->statistics.read(iprot);
          this->__isset.statistics = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      default:
        xfer += iprot->skip(ftype);
        break;
    }
    xfer += iprot->readFieldEnd();
  }

  xfer += iprot->readStructEnd();

  if (!isset_num_values)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  if (!isset_encoding)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  if (!isset_definition_level_encoding)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  if (!isset_repetition_level_encoding)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  return xfer;
}

uint32_t DataPageHeader::write(::apache::thrift::protocol::TProtocol* oprot) const {
  uint32_t xfer = 0;
  ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
  xfer += oprot->writeStructBegin("DataPageHeader");

  xfer += oprot->writeFieldBegin("num_values", ::apache::thrift::protocol::T_I32, 1);
  xfer += oprot->writeI32(this->num_values);
  xfer += oprot->writeFieldEnd();

  xfer += oprot->writeFieldBegin("encoding", ::apache::thrift::protocol::T_I32, 2);
  xfer += oprot->writeI32((int32_t)this->encoding);
  xfer += oprot->writeFieldEnd();

  xfer += oprot->writeFieldBegin("definition_level_encoding", ::apache::thrift::protocol::T_I32, 3);
  xfer += oprot->writeI32((int32_t)this->definition_level_encoding);
  xfer += oprot->writeFieldEnd();

  xfer += oprot->writeFieldBegin("repetition_level_encoding", ::apache::thrift::protocol::T_I32, 4);
  xfer += oprot->writeI32((int32_t)this->repetition_level_encoding);
  xfer += oprot->writeFieldEnd();

  if (this->__isset.statistics) {
    xfer += oprot->writeFieldBegin("statistics", ::apache::thrift::protocol::T_STRUCT, 5);
    xfer += this->statistics.write(oprot);
    xfer += oprot->writeFieldEnd();
  }
  xfer += oprot->writeFieldStop();
  xfer += oprot->writeStructEnd();
  return xfer;
}

void swap(DataPageHeader &a, DataPageHeader &b) {
  using ::std::swap;
  swap(a.num_values, b.num_values);
  swap(a.encoding, b.encoding);
  swap(a.definition_level_encoding, b.definition_level_encoding);
  swap(a.repetition_level_encoding, b.repetition_level_encoding);
  swap(a.statistics, b.statistics);
  swap(a.__isset, b.__isset);
}

DataPageHeader::DataPageHeader(const DataPageHeader& other46) {
  num_values = other46.num_values;
  encoding = other46.encoding;
  definition_level_encoding = other46.definition_level_encoding;
  repetition_level_encoding = other46.repetition_level_encoding;
  statistics = other46.statistics;
  __isset = other46.__isset;
}
DataPageHeader& DataPageHeader::operator=(const DataPageHeader& other47) {
  num_values = other47.num_values;
  encoding = other47.encoding;
  definition_level_encoding = other47.definition_level_encoding;
  repetition_level_encoding = other47.repetition_level_encoding;
  statistics = other47.statistics;
  __isset = other47.__isset;
  return *this;
}
void DataPageHeader::printTo(std::ostream& out) const {
  using ::apache::thrift::to_string;
  out << "DataPageHeader(";
  out << "num_values=" << to_string(num_values);
  out << ", " << "encoding=" << to_string(encoding);
  out << ", " << "definition_level_encoding=" << to_string(definition_level_encoding);
  out << ", " << "repetition_level_encoding=" << to_string(repetition_level_encoding);
  out << ", " << "statistics="; (__isset.statistics ? (out << to_string(statistics)) : (out << "<null>"));
  out << ")";
}


IndexPageHeader::~IndexPageHeader() throw() {
}

std::ostream& operator<<(std::ostream& out, const IndexPageHeader& obj)
{
  obj.printTo(out);
  return out;
}


uint32_t IndexPageHeader::read(::apache::thrift::protocol::TProtocol* iprot) {

  ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
  uint32_t xfer = 0;
  std::string fname;
  ::apache::thrift::protocol::TType ftype;
  int16_t fid;

  xfer += iprot->readStructBegin(fname);

  using ::apache::thrift::protocol::TProtocolException;


  while (true)
  {
    xfer += iprot->readFieldBegin(fname, ftype, fid);
    if (ftype == ::apache::thrift::protocol::T_STOP) {
      break;
    }
    xfer += iprot->skip(ftype);
    xfer += iprot->readFieldEnd();
  }

  xfer += iprot->readStructEnd();

  return xfer;
}

uint32_t IndexPageHeader::write(::apache::thrift::protocol::TProtocol* oprot) const {
  uint32_t xfer = 0;
  ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
  xfer += oprot->writeStructBegin("IndexPageHeader");

  xfer += oprot->writeFieldStop();
  xfer += oprot->writeStructEnd();
  return xfer;
}

void swap(IndexPageHeader &a, IndexPageHeader &b) {
  using ::std::swap;
  (void) a;
  (void) b;
}

IndexPageHeader::IndexPageHeader(const IndexPageHeader& other48) {
  (void) other48;
}
IndexPageHeader& IndexPageHeader::operator=(const IndexPageHeader& other49) {
  (void) other49;
  return *this;
}
void IndexPageHeader::printTo(std::ostream& out) const {
  using ::apache::thrift::to_string;
  out << "IndexPageHeader(";
  out << ")";
}


DictionaryPageHeader::~DictionaryPageHeader() throw() {
}


void DictionaryPageHeader::__set_num_values(const int32_t val) {
  this->num_values = val;
}

void DictionaryPageHeader::__set_encoding(const Encoding::type val) {
  this->encoding = val;
}

void DictionaryPageHeader::__set_is_sorted(const bool val) {
  this->is_sorted = val;
__isset.is_sorted = true;
}
std::ostream& operator<<(std::ostream& out, const DictionaryPageHeader& obj)
{
  obj.printTo(out);
  return out;
}


uint32_t DictionaryPageHeader::read(::apache::thrift::protocol::TProtocol* iprot) {

  ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
  uint32_t xfer = 0;
  std::string fname;
  ::apache::thrift::protocol::TType ftype;
  int16_t fid;

  xfer += iprot->readStructBegin(fname);

  using ::apache::thrift::protocol::TProtocolException;

  bool isset_num_values = false;
  bool isset_encoding = false;

  while (true)
  {
    xfer += iprot->readFieldBegin(fname, ftype, fid);
    if (ftype == ::apache::thrift::protocol::T_STOP) {
      break;
    }
    switch (fid)
    {
      case 1:
        if (ftype == ::apache::thrift::protocol::T_I32) {
          xfer += iprot->readI32(this->num_values);
          isset_num_values = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 2:
        if (ftype == ::apache::thrift::protocol::T_I32) {
          int32_t ecast50;
          xfer += iprot->readI32(ecast50);
          this->encoding = (Encoding::type)ecast50;
          isset_encoding = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 3:
        if (ftype == ::apache::thrift::protocol::T_BOOL) {
          xfer += iprot->readBool(this->is_sorted);
          this->__isset.is_sorted = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      default:
        xfer += iprot->skip(ftype);
        break;
    }
    xfer += iprot->readFieldEnd();
  }

  xfer += iprot->readStructEnd();

  if (!isset_num_values)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  if (!isset_encoding)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  return xfer;
}

uint32_t DictionaryPageHeader::write(::apache::thrift::protocol::TProtocol* oprot) const {
  uint32_t xfer = 0;
  ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
  xfer += oprot->writeStructBegin("DictionaryPageHeader");

  xfer += oprot->writeFieldBegin("num_values", ::apache::thrift::protocol::T_I32, 1);
  xfer += oprot->writeI32(this->num_values);
  xfer += oprot->writeFieldEnd();

  xfer += oprot->writeFieldBegin("encoding", ::apache::thrift::protocol::T_I32, 2);
  xfer += oprot->writeI32((int32_t)this->encoding);
  xfer += oprot->writeFieldEnd();

  if (this->__isset.is_sorted) {
    xfer += oprot->writeFieldBegin("is_sorted", ::apache::thrift::protocol::T_BOOL, 3);
    xfer += oprot->writeBool(this->is_sorted);
    xfer += oprot->writeFieldEnd();
  }
  xfer += oprot->writeFieldStop();
  xfer += oprot->writeStructEnd();
  return xfer;
}

void swap(DictionaryPageHeader &a, DictionaryPageHeader &b) {
  using ::std::swap;
  swap(a.num_values, b.num_values);
  swap(a.encoding, b.encoding);
  swap(a.is_sorted, b.is_sorted);
  swap(a.__isset, b.__isset);
}

DictionaryPageHeader::DictionaryPageHeader(const DictionaryPageHeader& other51) {
  num_values = other51.num_values;
  encoding = other51.encoding;
  is_sorted = other51.is_sorted;
  __isset = other51.__isset;
}
DictionaryPageHeader& DictionaryPageHeader::operator=(const DictionaryPageHeader& other52) {
  num_values = other52.num_values;
  encoding = other52.encoding;
  is_sorted = other52.is_sorted;
  __isset = other52.__isset;
  return *this;
}
void DictionaryPageHeader::printTo(std::ostream& out) const {
  using ::apache::thrift::to_string;
  out << "DictionaryPageHeader(";
  out << "num_values=" << to_string(num_values);
  out << ", " << "encoding=" << to_string(encoding);
  out << ", " << "is_sorted="; (__isset.is_sorted ? (out << to_string(is_sorted)) : (out << "<null>"));
  out << ")";
}


DataPageHeaderV2::~DataPageHeaderV2() throw() {
}


void DataPageHeaderV2::__set_num_values(const int32_t val) {
  this->num_values = val;
}

void DataPageHeaderV2::__set_num_nulls(const int32_t val) {
  this->num_nulls = val;
}

void DataPageHeaderV2::__set_num_rows(const int32_t val) {
  this->num_rows = val;
}

void DataPageHeaderV2::__set_encoding(const Encoding::type val) {
  this->encoding = val;
}

void DataPageHeaderV2::__set_definition_levels_byte_length(const int32_t val) {
  this->definition_levels_byte_length = val;
}

void DataPageHeaderV2::__set_repetition_levels_byte_length(const int32_t val) {
  this->repetition_levels_byte_length = val;
}

void DataPageHeaderV2::__set_is_compressed(const bool val) {
  this->is_compressed = val;
__isset.is_compressed = true;
}

void DataPageHeaderV2::__set_statistics(const Statistics& val) {
  this->statistics = val;
__isset.statistics = true;
}
std::ostream& operator<<(std::ostream& out, const DataPageHeaderV2& obj)
{
  obj.printTo(out);
  return out;
}


uint32_t DataPageHeaderV2::read(::apache::thrift::protocol::TProtocol* iprot) {

  ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
  uint32_t xfer = 0;
  std::string fname;
  ::apache::thrift::protocol::TType ftype;
  int16_t fid;

  xfer += iprot->readStructBegin(fname);

  using ::apache::thrift::protocol::TProtocolException;

  bool isset_num_values = false;
  bool isset_num_nulls = false;
  bool isset_num_rows = false;
  bool isset_encoding = false;
  bool isset_definition_levels_byte_length = false;
  bool isset_repetition_levels_byte_length = false;

  while (true)
  {
    xfer += iprot->readFieldBegin(fname, ftype, fid);
    if (ftype == ::apache::thrift::protocol::T_STOP) {
      break;
    }
    switch (fid)
    {
      case 1:
        if (ftype == ::apache::thrift::protocol::T_I32) {
          xfer += iprot->readI32(this->num_values);
          isset_num_values = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 2:
        if (ftype == ::apache::thrift::protocol::T_I32) {
          xfer += iprot->readI32(this->num_nulls);
          isset_num_nulls = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 3:
        if (ftype == ::apache::thrift::protocol::T_I32) {
          xfer += iprot->readI32(this->num_rows);
          isset_num_rows = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 4:
        if (ftype == ::apache::thrift::protocol::T_I32) {
          int32_t ecast53;
          xfer += iprot->readI32(ecast53);
          this->encoding = (Encoding::type)ecast53;
          isset_encoding = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 5:
        if (ftype == ::apache::thrift::protocol::T_I32) {
          xfer += iprot->readI32(this->definition_levels_byte_length);
          isset_definition_levels_byte_length = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 6:
        if (ftype == ::apache::thrift::protocol::T_I32) {
          xfer += iprot->readI32(this->repetition_levels_byte_length);
          isset_repetition_levels_byte_length = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 7:
        if (ftype == ::apache::thrift::protocol::T_BOOL) {
          xfer += iprot->readBool(this->is_compressed);
          this->__isset.is_compressed = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 8:
        if (ftype == ::apache::thrift::protocol::T_STRUCT) {
          xfer += this->statistics.read(iprot);
          this->__isset.statistics = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      default:
        xfer += iprot->skip(ftype);
        break;
    }
    xfer += iprot->readFieldEnd();
  }

  xfer += iprot->readStructEnd();

  if (!isset_num_values)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  if (!isset_num_nulls)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  if (!isset_num_rows)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  if (!isset_encoding)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  if (!isset_definition_levels_byte_length)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  if (!isset_repetition_levels_byte_length)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  return xfer;
}

uint32_t DataPageHeaderV2::write(::apache::thrift::protocol::TProtocol* oprot) const {
  uint32_t xfer = 0;
  ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
  xfer += oprot->writeStructBegin("DataPageHeaderV2");

  xfer += oprot->writeFieldBegin("num_values", ::apache::thrift::protocol::T_I32, 1);
  xfer += oprot->writeI32(this->num_values);
  xfer += oprot->writeFieldEnd();

  xfer += oprot->writeFieldBegin("num_nulls", ::apache::thrift::protocol::T_I32, 2);
  xfer += oprot->writeI32(this->num_nulls);
  xfer += oprot->writeFieldEnd();

  xfer += oprot->writeFieldBegin("num_rows", ::apache::thrift::protocol::T_I32, 3);
  xfer += oprot->writeI32(this->num_rows);
  xfer += oprot->writeFieldEnd();

  xfer += oprot->writeFieldBegin("encoding", ::apache::thrift::protocol::T_I32, 4);
  xfer += oprot->writeI32((int32_t)this->encoding);
  xfer += oprot->writeFieldEnd();

  xfer += oprot->writeFieldBegin("definition_levels_byte_length", ::apache::thrift::protocol::T_I32, 5);
  xfer += oprot->writeI32(this->definition_levels_byte_length);
  xfer += oprot->writeFieldEnd();

  xfer += oprot->writeFieldBegin("repetition_levels_byte_length", ::apache::thrift::protocol::T_I32, 6);
  xfer += oprot->writeI32(this->repetition_levels_byte_length);
  xfer += oprot->writeFieldEnd();

  if (this->__isset.is_compressed) {
    xfer += oprot->writeFieldBegin("is_compressed", ::apache::thrift::protocol::T_BOOL, 7);
    xfer += oprot->writeBool(this->is_compressed);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.statistics) {
    xfer += oprot->writeFieldBegin("statistics", ::apache::thrift::protocol::T_STRUCT, 8);
    xfer += this->statistics.write(oprot);
    xfer += oprot->writeFieldEnd();
  }
  xfer += oprot->writeFieldStop();
  xfer += oprot->writeStructEnd();
  return xfer;
}

void swap(DataPageHeaderV2 &a, DataPageHeaderV2 &b) {
  using ::std::swap;
  swap(a.num_values, b.num_values);
  swap(a.num_nulls, b.num_nulls);
  swap(a.num_rows, b.num_rows);
  swap(a.encoding, b.encoding);
  swap(a.definition_levels_byte_length, b.definition_levels_byte_length);
  swap(a.repetition_levels_byte_length, b.repetition_levels_byte_length);
  swap(a.is_compressed, b.is_compressed);
  swap(a.statistics, b.statistics);
  swap(a.__isset, b.__isset);
}

DataPageHeaderV2::DataPageHeaderV2(const DataPageHeaderV2& other54) {
  num_values = other54.num_values;
  num_nulls = other54.num_nulls;
  num_rows = other54.num_rows;
  encoding = other54.encoding;
  definition_levels_byte_length = other54.definition_levels_byte_length;
  repetition_levels_byte_length = other54.repetition_levels_byte_length;
  is_compressed = other54.is_compressed;
  statistics = other54.statistics;
  __isset = other54.__isset;
}
DataPageHeaderV2& DataPageHeaderV2::operator=(const DataPageHeaderV2& other55) {
  num_values = other55.num_values;
  num_nulls = other55.num_nulls;
  num_rows = other55.num_rows;
  encoding = other55.encoding;
  definition_levels_byte_length = other55.definition_levels_byte_length;
  repetition_levels_byte_length = other55.repetition_levels_byte_length;
  is_compressed = other55.is_compressed;
  statistics = other55.statistics;
  __isset = other55.__isset;
  return *this;
}
void DataPageHeaderV2::printTo(std::ostream& out) const {
  using ::apache::thrift::to_string;
  out << "DataPageHeaderV2(";
  out << "num_values=" << to_string(num_values);
  out << ", " << "num_nulls=" << to_string(num_nulls);
  out << ", " << "num_rows=" << to_string(num_rows);
  out << ", " << "encoding=" << to_string(encoding);
  out << ", " << "definition_levels_byte_length=" << to_string(definition_levels_byte_length);
  out << ", " << "repetition_levels_byte_length=" << to_string(repetition_levels_byte_length);
  out << ", " << "is_compressed="; (__isset.is_compressed ? (out << to_string(is_compressed)) : (out << "<null>"));
  out << ", " << "statistics="; (__isset.statistics ? (out << to_string(statistics)) : (out << "<null>"));
  out << ")";
}


PageHeader::~PageHeader() throw() {
}


void PageHeader::__set_type(const PageType::type val) {
  this->type = val;
}

void PageHeader::__set_uncompressed_page_size(const int32_t val) {
  this->uncompressed_page_size = val;
}

void PageHeader::__set_compressed_page_size(const int32_t val) {
  this->compressed_page_size = val;
}

void PageHeader::__set_crc(const int32_t val) {
  this->crc = val;
__isset.crc = true;
}

void PageHeader::__set_data_page_header(const DataPageHeader& val) {
  this->data_page_header = val;
__isset.data_page_header = true;
}

void PageHeader::__set_index_page_header(const IndexPageHeader& val) {
  this->index_page_header = val;
__isset.index_page_header = true;
}

void PageHeader::__set_dictionary_page_header(const DictionaryPageHeader& val) {
  this->dictionary_page_header = val;
__isset.dictionary_page_header = true;
}

void PageHeader::__set_data_page_header_v2(const DataPageHeaderV2& val) {
  this->data_page_header_v2 = val;
__isset.data_page_header_v2 = true;
}
std::ostream& operator<<(std::ostream& out, const PageHeader& obj)
{
  obj.printTo(out);
  return out;
}


uint32_t PageHeader::read(::apache::thrift::protocol::TProtocol* iprot) {

  ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
  uint32_t xfer = 0;
  std::string fname;
  ::apache::thrift::protocol::TType ftype;
  int16_t fid;

  xfer += iprot->readStructBegin(fname);

  using ::apache::thrift::protocol::TProtocolException;

  bool isset_type = false;
  bool isset_uncompressed_page_size = false;
  bool isset_compressed_page_size = false;

  while (true)
  {
    xfer += iprot->readFieldBegin(fname, ftype, fid);
    if (ftype == ::apache::thrift::protocol::T_STOP) {
      break;
    }
    switch (fid)
    {
      case 1:
        if (ftype == ::apache::thrift::protocol::T_I32) {
          int32_t ecast56;
          xfer += iprot->readI32(ecast56);
          this->type = (PageType::type)ecast56;
          isset_type = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 2:
        if (ftype == ::apache::thrift::protocol::T_I32) {
          xfer += iprot->readI32(this->uncompressed_page_size);
          isset_uncompressed_page_size = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 3:
        if (ftype == ::apache::thrift::protocol::T_I32) {
          xfer += iprot->readI32(this->compressed_page_size);
          isset_compressed_page_size = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 4:
        if (ftype == ::apache::thrift::protocol::T_I32) {
          xfer += iprot->readI32(this->crc);
          this->__isset.crc = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 5:
        if (ftype == ::apache::thrift::protocol::T_STRUCT) {
          xfer += this->data_page_header.read(iprot);
          this->__isset.data_page_header = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 6:
        if (ftype == ::apache::thrift::protocol::T_STRUCT) {
          xfer += this->index_page_header.read(iprot);
          this->__isset.index_page_header = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 7:
        if (ftype == ::apache::thrift::protocol::T_STRUCT) {
          xfer += this->dictionary_page_header.read(iprot);
          this->__isset.dictionary_page_header = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 8:
        if (ftype == ::apache::thrift::protocol::T_STRUCT) {
          xfer += this->data_page_header_v2.read(iprot);
          this->__isset.data_page_header_v2 = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      default:
        xfer += iprot->skip(ftype);
        break;
    }
    xfer += iprot->readFieldEnd();
  }

  xfer += iprot->readStructEnd();

  if (!isset_type)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  if (!isset_uncompressed_page_size)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  if (!isset_compressed_page_size)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  return xfer;
}

uint32_t PageHeader::write(::apache::thrift::protocol::TProtocol* oprot) const {
  uint32_t xfer = 0;
  ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
  xfer += oprot->writeStructBegin("PageHeader");

  xfer += oprot->writeFieldBegin("type", ::apache::thrift::protocol::T_I32, 1);
  xfer += oprot->writeI32((int32_t)this->type);
  xfer += oprot->writeFieldEnd();

  xfer += oprot->writeFieldBegin("uncompressed_page_size", ::apache::thrift::protocol::T_I32, 2);
  xfer += oprot->writeI32(this->uncompressed_page_size);
  xfer += oprot->writeFieldEnd();

  xfer += oprot->writeFieldBegin("compressed_page_size", ::apache::thrift::protocol::T_I32, 3);
  xfer += oprot->writeI32(this->compressed_page_size);
  xfer += oprot->writeFieldEnd();

  if (this->__isset.crc) {
    xfer += oprot->writeFieldBegin("crc", ::apache::thrift::protocol::T_I32, 4);
    xfer += oprot->writeI32(this->crc);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.data_page_header) {
    xfer += oprot->writeFieldBegin("data_page_header", ::apache::thrift::protocol::T_STRUCT, 5);
    xfer += this->data_page_header.write(oprot);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.index_page_header) {
    xfer += oprot->writeFieldBegin("index_page_header", ::apache::thrift::protocol::T_STRUCT, 6);
    xfer += this->index_page_header.write(oprot);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.dictionary_page_header) {
    xfer += oprot->writeFieldBegin("dictionary_page_header", ::apache::thrift::protocol::T_STRUCT, 7);
    xfer += this->dictionary_page_header.write(oprot);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.data_page_header_v2) {
    xfer += oprot->writeFieldBegin("data_page_header_v2", ::apache::thrift::protocol::T_STRUCT, 8);
    xfer += this->data_page_header_v2.write(oprot);
    xfer += oprot->writeFieldEnd();
  }
  xfer += oprot->writeFieldStop();
  xfer += oprot->writeStructEnd();
  return xfer;
}

void swap(PageHeader &a, PageHeader &b) {
  using ::std::swap;
  swap(a.type, b.type);
  swap(a.uncompressed_page_size, b.uncompressed_page_size);
  swap(a.compressed_page_size, b.compressed_page_size);
  swap(a.crc, b.crc);
  swap(a.data_page_header, b.data_page_header);
  swap(a.index_page_header, b.index_page_header);
  swap(a.dictionary_page_header, b.dictionary_page_header);
  swap(a.data_page_header_v2, b.data_page_header_v2);
  swap(a.__isset, b.__isset);
}

PageHeader::PageHeader(const PageHeader& other57) {
  type = other57.type;
  uncompressed_page_size = other57.uncompressed_page_size;
  compressed_page_size = other57.compressed_page_size;
  crc = other57.crc;
  data_page_header = other57.data_page_header;
  index_page_header = other57.index_page_header;
  dictionary_page_header = other57.dictionary_page_header;
  data_page_header_v2 = other57.data_page_header_v2;
  __isset = other57.__isset;
}
PageHeader& PageHeader::operator=(const PageHeader& other58) {
  type = other58.type;
  uncompressed_page_size = other58.uncompressed_page_size;
  compressed_page_size = other58.compressed_page_size;
  crc = other58.crc;
  data_page_header = other58.data_page_header;
  index_page_header = other58.index_page_header;
  dictionary_page_header = other58.dictionary_page_header;
  data_page_header_v2 = other58.data_page_header_v2;
  __isset = other58.__isset;
  return *this;
}
void PageHeader::printTo(std::ostream& out) const {
  using ::apache::thrift::to_string;
  out << "PageHeader(";
  out << "type=" << to_string(type);
  out << ", " << "uncompressed_page_size=" << to_string(uncompressed_page_size);
  out << ", " << "compressed_page_size=" << to_string(compressed_page_size);
  out << ", " << "crc="; (__isset.crc ? (out << to_string(crc)) : (out << "<null>"));
  out << ", " << "data_page_header="; (__isset.data_page_header ? (out << to_string(data_page_header)) : (out << "<null>"));
  out << ", " << "index_page_header="; (__isset.index_page_header ? (out << to_string(index_page_header)) : (out << "<null>"));
  out << ", " << "dictionary_page_header="; (__isset.dictionary_page_header ? (out << to_string(dictionary_page_header)) : (out << "<null>"));
  out << ", " << "data_page_header_v2="; (__isset.data_page_header_v2 ? (out << to_string(data_page_header_v2)) : (out << "<null>"));
  out << ")";
}


KeyValue::~KeyValue() throw() {
}


void KeyValue::__set_key(const std::string& val) {
  this->key = val;
}

void KeyValue::__set_value(const std::string& val) {
  this->value = val;
__isset.value = true;
}
std::ostream& operator<<(std::ostream& out, const KeyValue& obj)
{
  obj.printTo(out);
  return out;
}


uint32_t KeyValue::read(::apache::thrift::protocol::TProtocol* iprot) {

  ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
  uint32_t xfer = 0;
  std::string fname;
  ::apache::thrift::protocol::TType ftype;
  int16_t fid;

  xfer += iprot->readStructBegin(fname);

  using ::apache::thrift::protocol::TProtocolException;

  bool isset_key = false;

  while (true)
  {
    xfer += iprot->readFieldBegin(fname, ftype, fid);
    if (ftype == ::apache::thrift::protocol::T_STOP) {
      break;
    }
    switch (fid)
    {
      case 1:
        if (ftype == ::apache::thrift::protocol::T_STRING) {
          xfer += iprot->readString(this->key);
          isset_key = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 2:
        if (ftype == ::apache::thrift::protocol::T_STRING) {
          xfer += iprot->readString(this->value);
          this->__isset.value = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      default:
        xfer += iprot->skip(ftype);
        break;
    }
    xfer += iprot->readFieldEnd();
  }

  xfer += iprot->readStructEnd();

  if (!isset_key)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  return xfer;
}

uint32_t KeyValue::write(::apache::thrift::protocol::TProtocol* oprot) const {
  uint32_t xfer = 0;
  ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
  xfer += oprot->writeStructBegin("KeyValue");

  xfer += oprot->writeFieldBegin("key", ::apache::thrift::protocol::T_STRING, 1);
  xfer += oprot->writeString(this->key);
  xfer += oprot->writeFieldEnd();

  if (this->__isset.value) {
    xfer += oprot->writeFieldBegin("value", ::apache::thrift::protocol::T_STRING, 2);
    xfer += oprot->writeString(this->value);
    xfer += oprot->writeFieldEnd();
  }
  xfer += oprot->writeFieldStop();
  xfer += oprot->writeStructEnd();
  return xfer;
}

void swap(KeyValue &a, KeyValue &b) {
  using ::std::swap;
  swap(a.key, b.key);
  swap(a.value, b.value);
  swap(a.__isset, b.__isset);
}

KeyValue::KeyValue(const KeyValue& other59) {
  key = other59.key;
  value = other59.value;
  __isset = other59.__isset;
}
KeyValue& KeyValue::operator=(const KeyValue& other60) {
  key = other60.key;
  value = other60.value;
  __isset = other60.__isset;
  return *this;
}
void KeyValue::printTo(std::ostream& out) const {
  using ::apache::thrift::to_string;
  out << "KeyValue(";
  out << "key=" << to_string(key);
  out << ", " << "value="; (__isset.value ? (out << to_string(value)) : (out << "<null>"));
  out << ")";
}


SortingColumn::~SortingColumn() throw() {
}


void SortingColumn::__set_column_idx(const int32_t val) {
  this->column_idx = val;
}

void SortingColumn::__set_descending(const bool val) {
  this->descending = val;
}

void SortingColumn::__set_nulls_first(const bool val) {
  this->nulls_first = val;
}
std::ostream& operator<<(std::ostream& out, const SortingColumn& obj)
{
  obj.printTo(out);
  return out;
}


uint32_t SortingColumn::read(::apache::thrift::protocol::TProtocol* iprot) {

  ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
  uint32_t xfer = 0;
  std::string fname;
  ::apache::thrift::protocol::TType ftype;
  int16_t fid;

  xfer += iprot->readStructBegin(fname);

  using ::apache::thrift::protocol::TProtocolException;

  bool isset_column_idx = false;
  bool isset_descending = false;
  bool isset_nulls_first = false;

  while (true)
  {
    xfer += iprot->readFieldBegin(fname, ftype, fid);
    if (ftype == ::apache::thrift::protocol::T_STOP) {
      break;
    }
    switch (fid)
    {
      case 1:
        if (ftype == ::apache::thrift::protocol::T_I32) {
          xfer += iprot->readI32(this->column_idx);
          isset_column_idx = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 2:
        if (ftype == ::apache::thrift::protocol::T_BOOL) {
          xfer += iprot->readBool(this->descending);
          isset_descending = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 3:
        if (ftype == ::apache::thrift::protocol::T_BOOL) {
          xfer += iprot->readBool(this->nulls_first);
          isset_nulls_first = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      default:
        xfer += iprot->skip(ftype);
        break;
    }
    xfer += iprot->readFieldEnd();
  }

  xfer += iprot->readStructEnd();

  if (!isset_column_idx)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  if (!isset_descending)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  if (!isset_nulls_first)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  return xfer;
}

uint32_t SortingColumn::write(::apache::thrift::protocol::TProtocol* oprot) const {
  uint32_t xfer = 0;
  ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
  xfer += oprot->writeStructBegin("SortingColumn");

  xfer += oprot->writeFieldBegin("column_idx", ::apache::thrift::protocol::T_I32, 1);
  xfer += oprot->writeI32(this->column_idx);
  xfer += oprot->writeFieldEnd();

  xfer += oprot->writeFieldBegin("descending", ::apache::thrift::protocol::T_BOOL, 2);
  xfer += oprot->writeBool(this->descending);
  xfer += oprot->writeFieldEnd();

  xfer += oprot->writeFieldBegin("nulls_first", ::apache::thrift::protocol::T_BOOL, 3);
  xfer += oprot->writeBool(this->nulls_first);
  xfer += oprot->writeFieldEnd();

  xfer += oprot->writeFieldStop();
  xfer += oprot->writeStructEnd();
  return xfer;
}

void swap(SortingColumn &a, SortingColumn &b) {
  using ::std::swap;
  swap(a.column_idx, b.column_idx);
  swap(a.descending, b.descending);
  swap(a.nulls_first, b.nulls_first);
}

SortingColumn::SortingColumn(const SortingColumn& other61) {
  column_idx = other61.column_idx;
  descending = other61.descending;
  nulls_first = other61.nulls_first;
}
SortingColumn& SortingColumn::operator=(const SortingColumn& other62) {
  column_idx = other62.column_idx;
  descending = other62.descending;
  nulls_first = other62.nulls_first;
  return *this;
}
void SortingColumn::printTo(std::ostream& out) const {
  using ::apache::thrift::to_string;
  out << "SortingColumn(";
  out << "column_idx=" << to_string(column_idx);
  out << ", " << "descending=" << to_string(descending);
  out << ", " << "nulls_first=" << to_string(nulls_first);
  out << ")";
}


PageEncodingStats::~PageEncodingStats() throw() {
}


void PageEncodingStats::__set_page_type(const PageType::type val) {
  this->page_type = val;
}

void PageEncodingStats::__set_encoding(const Encoding::type val) {
  this->encoding = val;
}

void PageEncodingStats::__set_count(const int32_t val) {
  this->count = val;
}
std::ostream& operator<<(std::ostream& out, const PageEncodingStats& obj)
{
  obj.printTo(out);
  return out;
}


uint32_t PageEncodingStats::read(::apache::thrift::protocol::TProtocol* iprot) {

  ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
  uint32_t xfer = 0;
  std::string fname;
  ::apache::thrift::protocol::TType ftype;
  int16_t fid;

  xfer += iprot->readStructBegin(fname);

  using ::apache::thrift::protocol::TProtocolException;

  bool isset_page_type = false;
  bool isset_encoding = false;
  bool isset_count = false;

  while (true)
  {
    xfer += iprot->readFieldBegin(fname, ftype, fid);
    if (ftype == ::apache::thrift::protocol::T_STOP) {
      break;
    }
    switch (fid)
    {
      case 1:
        if (ftype == ::apache::thrift::protocol::T_I32) {
          int32_t ecast63;
          xfer += iprot->readI32(ecast63);
          this->page_type = (PageType::type)ecast63;
          isset_page_type = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 2:
        if (ftype == ::apache::thrift::protocol::T_I32) {
          int32_t ecast64;
          xfer += iprot->readI32(ecast64);
          this->encoding = (Encoding::type)ecast64;
          isset_encoding = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 3:
        if (ftype == ::apache::thrift::protocol::T_I32) {
          xfer += iprot->readI32(this->count);
          isset_count = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      default:
        xfer += iprot->skip(ftype);
        break;
    }
    xfer += iprot->readFieldEnd();
  }

  xfer += iprot->readStructEnd();

  if (!isset_page_type)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  if (!isset_encoding)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  if (!isset_count)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  return xfer;
}

uint32_t PageEncodingStats::write(::apache::thrift::protocol::TProtocol* oprot) const {
  uint32_t xfer = 0;
  ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
  xfer += oprot->writeStructBegin("PageEncodingStats");

  xfer += oprot->writeFieldBegin("page_type", ::apache::thrift::protocol::T_I32, 1);
  xfer += oprot->writeI32((int32_t)this->page_type);
  xfer += oprot->writeFieldEnd();

  xfer += oprot->writeFieldBegin("encoding", ::apache::thrift::protocol::T_I32, 2);
  xfer += oprot->writeI32((int32_t)this->encoding);
  xfer += oprot->writeFieldEnd();

  xfer += oprot->writeFieldBegin("count", ::apache::thrift::protocol::T_I32, 3);
  xfer += oprot->writeI32(this->count);
  xfer += oprot->writeFieldEnd();

  xfer += oprot->writeFieldStop();
  xfer += oprot->writeStructEnd();
  return xfer;
}

void swap(PageEncodingStats &a, PageEncodingStats &b) {
  using ::std::swap;
  swap(a.page_type, b.page_type);
  swap(a.encoding, b.encoding);
  swap(a.count, b.count);
}

PageEncodingStats::PageEncodingStats(const PageEncodingStats& other65) {
  page_type = other65.page_type;
  encoding = other65.encoding;
  count = other65.count;
}
PageEncodingStats& PageEncodingStats::operator=(const PageEncodingStats& other66) {
  page_type = other66.page_type;
  encoding = other66.encoding;
  count = other66.count;
  return *this;
}
void PageEncodingStats::printTo(std::ostream& out) const {
  using ::apache::thrift::to_string;
  out << "PageEncodingStats(";
  out << "page_type=" << to_string(page_type);
  out << ", " << "encoding=" << to_string(encoding);
  out << ", " << "count=" << to_string(count);
  out << ")";
}


ColumnMetaData::~ColumnMetaData() throw() {
}


void ColumnMetaData::__set_type(const Type::type val) {
  this->type = val;
}

void ColumnMetaData::__set_encodings(const std::vector<Encoding::type> & val) {
  this->encodings = val;
}

void ColumnMetaData::__set_path_in_schema(const std::vector<std::string> & val) {
  this->path_in_schema = val;
}

void ColumnMetaData::__set_codec(const CompressionCodec::type val) {
  this->codec = val;
}

void ColumnMetaData::__set_num_values(const int64_t val) {
  this->num_values = val;
}

void ColumnMetaData::__set_total_uncompressed_size(const int64_t val) {
  this->total_uncompressed_size = val;
}

void ColumnMetaData::__set_total_compressed_size(const int64_t val) {
  this->total_compressed_size = val;
}

void ColumnMetaData::__set_key_value_metadata(const std::vector<KeyValue> & val) {
  this->key_value_metadata = val;
__isset.key_value_metadata = true;
}

void ColumnMetaData::__set_data_page_offset(const int64_t val) {
  this->data_page_offset = val;
}

void ColumnMetaData::__set_index_page_offset(const int64_t val) {
  this->index_page_offset = val;
__isset.index_page_offset = true;
}

void ColumnMetaData::__set_dictionary_page_offset(const int64_t val) {
  this->dictionary_page_offset = val;
__isset.dictionary_page_offset = true;
}

void ColumnMetaData::__set_statistics(const Statistics& val) {
  this->statistics = val;
__isset.statistics = true;
}

void ColumnMetaData::__set_encoding_stats(const std::vector<PageEncodingStats> & val) {
  this->encoding_stats = val;
__isset.encoding_stats = true;
}
std::ostream& operator<<(std::ostream& out, const ColumnMetaData& obj)
{
  obj.printTo(out);
  return out;
}


uint32_t ColumnMetaData::read(::apache::thrift::protocol::TProtocol* iprot) {

  ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
  uint32_t xfer = 0;
  std::string fname;
  ::apache::thrift::protocol::TType ftype;
  int16_t fid;

  xfer += iprot->readStructBegin(fname);

  using ::apache::thrift::protocol::TProtocolException;

  bool isset_type = false;
  bool isset_encodings = false;
  bool isset_path_in_schema = false;
  bool isset_codec = false;
  bool isset_num_values = false;
  bool isset_total_uncompressed_size = false;
  bool isset_total_compressed_size = false;
  bool isset_data_page_offset = false;

  while (true)
  {
    xfer += iprot->readFieldBegin(fname, ftype, fid);
    if (ftype == ::apache::thrift::protocol::T_STOP) {
      break;
    }
    switch (fid)
    {
      case 1:
        if (ftype == ::apache::thrift::protocol::T_I32) {
          int32_t ecast67;
          xfer += iprot->readI32(ecast67);
          this->type = (Type::type)ecast67;
          isset_type = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 2:
        if (ftype == ::apache::thrift::protocol::T_LIST) {
          {
            this->encodings.clear();
            uint32_t _size68;
            ::apache::thrift::protocol::TType _etype71;
            xfer += iprot->readListBegin(_etype71, _size68);
            this->encodings.resize(_size68);
            uint32_t _i72;
            for (_i72 = 0; _i72 < _size68; ++_i72)
            {
              int32_t ecast73;
              xfer += iprot->readI32(ecast73);
              this->encodings[_i72] = (Encoding::type)ecast73;
            }
            xfer += iprot->readListEnd();
          }
          isset_encodings = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 3:
        if (ftype == ::apache::thrift::protocol::T_LIST) {
          {
            this->path_in_schema.clear();
            uint32_t _size74;
            ::apache::thrift::protocol::TType _etype77;
            xfer += iprot->readListBegin(_etype77, _size74);
            this->path_in_schema.resize(_size74);
            uint32_t _i78;
            for (_i78 = 0; _i78 < _size74; ++_i78)
            {
              xfer += iprot->readString(this->path_in_schema[_i78]);
            }
            xfer += iprot->readListEnd();
          }
          isset_path_in_schema = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 4:
        if (ftype == ::apache::thrift::protocol::T_I32) {
          int32_t ecast79;
          xfer += iprot->readI32(ecast79);
          this->codec = (CompressionCodec::type)ecast79;
          isset_codec = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 5:
        if (ftype == ::apache::thrift::protocol::T_I64) {
          xfer += iprot->readI64(this->num_values);
          isset_num_values = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 6:
        if (ftype == ::apache::thrift::protocol::T_I64) {
          xfer += iprot->readI64(this->total_uncompressed_size);
          isset_total_uncompressed_size = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 7:
        if (ftype == ::apache::thrift::protocol::T_I64) {
          xfer += iprot->readI64(this->total_compressed_size);
          isset_total_compressed_size = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 8:
        if (ftype == ::apache::thrift::protocol::T_LIST) {
          {
            this->key_value_metadata.clear();
            uint32_t _size80;
            ::apache::thrift::protocol::TType _etype83;
            xfer += iprot->readListBegin(_etype83, _size80);
            this->key_value_metadata.resize(_size80);
            uint32_t _i84;
            for (_i84 = 0; _i84 < _size80; ++_i84)
            {
              xfer += this->key_value_metadata[_i84].read(iprot);
            }
            xfer += iprot->readListEnd();
          }
          this->__isset.key_value_metadata = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 9:
        if (ftype == ::apache::thrift::protocol::T_I64) {
          xfer += iprot->readI64(this->data_page_offset);
          isset_data_page_offset = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 10:
        if (ftype == ::apache::thrift::protocol::T_I64) {
          xfer += iprot->readI64(this->index_page_offset);
          this->__isset.index_page_offset = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 11:
        if (ftype == ::apache::thrift::protocol::T_I64) {
          xfer += iprot->readI64(this->dictionary_page_offset);
          this->__isset.dictionary_page_offset = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 12:
        if (ftype == ::apache::thrift::protocol::T_STRUCT) {
          xfer += this->statistics.read(iprot);
          this->__isset.statistics = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 13:
        if (ftype == ::apache::thrift::protocol::T_LIST) {
          {
            this->encoding_stats.clear();
            uint32_t _size85;
            ::apache::thrift::protocol::TType _etype88;
            xfer += iprot->readListBegin(_etype88, _size85);
            this->encoding_stats.resize(_size85);
            uint32_t _i89;
            for (_i89 = 0; _i89 < _size85; ++_i89)
            {
              xfer += this->encoding_stats[_i89].read(iprot);
            }
            xfer += iprot->readListEnd();
          }
          this->__isset.encoding_stats = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      default:
        xfer += iprot->skip(ftype);
        break;
    }
    xfer += iprot->readFieldEnd();
  }

  xfer += iprot->readStructEnd();

  if (!isset_type)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  if (!isset_encodings)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  if (!isset_path_in_schema)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  if (!isset_codec)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  if (!isset_num_values)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  if (!isset_total_uncompressed_size)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  if (!isset_total_compressed_size)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  if (!isset_data_page_offset)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  return xfer;
}

uint32_t ColumnMetaData::write(::apache::thrift::protocol::TProtocol* oprot) const {
  uint32_t xfer = 0;
  ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
  xfer += oprot->writeStructBegin("ColumnMetaData");

  xfer += oprot->writeFieldBegin("type", ::apache::thrift::protocol::T_I32, 1);
  xfer += oprot->writeI32((int32_t)this->type);
  xfer += oprot->writeFieldEnd();

  xfer += oprot->writeFieldBegin("encodings", ::apache::thrift::protocol::T_LIST, 2);
  {
    xfer += oprot->writeListBegin(::apache::thrift::protocol::T_I32, static_cast<uint32_t>(this->encodings.size()));
    std::vector<Encoding::type> ::const_iterator _iter90;
    for (_iter90 = this->encodings.begin(); _iter90 != this->encodings.end(); ++_iter90)
    {
      xfer += oprot->writeI32((int32_t)(*_iter90));
    }
    xfer += oprot->writeListEnd();
  }
  xfer += oprot->writeFieldEnd();

  xfer += oprot->writeFieldBegin("path_in_schema", ::apache::thrift::protocol::T_LIST, 3);
  {
    xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRING, static_cast<uint32_t>(this->path_in_schema.size()));
    std::vector<std::string> ::const_iterator _iter91;
    for (_iter91 = this->path_in_schema.begin(); _iter91 != this->path_in_schema.end(); ++_iter91)
    {
      xfer += oprot->writeString((*_iter91));
    }
    xfer += oprot->writeListEnd();
  }
  xfer += oprot->writeFieldEnd();

  xfer += oprot->writeFieldBegin("codec", ::apache::thrift::protocol::T_I32, 4);
  xfer += oprot->writeI32((int32_t)this->codec);
  xfer += oprot->writeFieldEnd();

  xfer += oprot->writeFieldBegin("num_values", ::apache::thrift::protocol::T_I64, 5);
  xfer += oprot->writeI64(this->num_values);
  xfer += oprot->writeFieldEnd();

  xfer += oprot->writeFieldBegin("total_uncompressed_size", ::apache::thrift::protocol::T_I64, 6);
  xfer += oprot->writeI64(this->total_uncompressed_size);
  xfer += oprot->writeFieldEnd();

  xfer += oprot->writeFieldBegin("total_compressed_size", ::apache::thrift::protocol::T_I64, 7);
  xfer += oprot->writeI64(this->total_compressed_size);
  xfer += oprot->writeFieldEnd();

  if (this->__isset.key_value_metadata) {
    xfer += oprot->writeFieldBegin("key_value_metadata", ::apache::thrift::protocol::T_LIST, 8);
    {
      xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, static_cast<uint32_t>(this->key_value_metadata.size()));
      std::vector<KeyValue> ::const_iterator _iter92;
      for (_iter92 = this->key_value_metadata.begin(); _iter92 != this->key_value_metadata.end(); ++_iter92)
      {
        xfer += (*_iter92).write(oprot);
      }
      xfer += oprot->writeListEnd();
    }
    xfer += oprot->writeFieldEnd();
  }
  xfer += oprot->writeFieldBegin("data_page_offset", ::apache::thrift::protocol::T_I64, 9);
  xfer += oprot->writeI64(this->data_page_offset);
  xfer += oprot->writeFieldEnd();

  if (this->__isset.index_page_offset) {
    xfer += oprot->writeFieldBegin("index_page_offset", ::apache::thrift::protocol::T_I64, 10);
    xfer += oprot->writeI64(this->index_page_offset);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.dictionary_page_offset) {
    xfer += oprot->writeFieldBegin("dictionary_page_offset", ::apache::thrift::protocol::T_I64, 11);
    xfer += oprot->writeI64(this->dictionary_page_offset);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.statistics) {
    xfer += oprot->writeFieldBegin("statistics", ::apache::thrift::protocol::T_STRUCT, 12);
    xfer += this->statistics.write(oprot);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.encoding_stats) {
    xfer += oprot->writeFieldBegin("encoding_stats", ::apache::thrift::protocol::T_LIST, 13);
    {
      xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, static_cast<uint32_t>(this->encoding_stats.size()));
      std::vector<PageEncodingStats> ::const_iterator _iter93;
      for (_iter93 = this->encoding_stats.begin(); _iter93 != this->encoding_stats.end(); ++_iter93)
      {
        xfer += (*_iter93).write(oprot);
      }
      xfer += oprot->writeListEnd();
    }
    xfer += oprot->writeFieldEnd();
  }
  xfer += oprot->writeFieldStop();
  xfer += oprot->writeStructEnd();
  return xfer;
}

void swap(ColumnMetaData &a, ColumnMetaData &b) {
  using ::std::swap;
  swap(a.type, b.type);
  swap(a.encodings, b.encodings);
  swap(a.path_in_schema, b.path_in_schema);
  swap(a.codec, b.codec);
  swap(a.num_values, b.num_values);
  swap(a.total_uncompressed_size, b.total_uncompressed_size);
  swap(a.total_compressed_size, b.total_compressed_size);
  swap(a.key_value_metadata, b.key_value_metadata);
  swap(a.data_page_offset, b.data_page_offset);
  swap(a.index_page_offset, b.index_page_offset);
  swap(a.dictionary_page_offset, b.dictionary_page_offset);
  swap(a.statistics, b.statistics);
  swap(a.encoding_stats, b.encoding_stats);
  swap(a.__isset, b.__isset);
}

ColumnMetaData::ColumnMetaData(const ColumnMetaData& other94) {
  type = other94.type;
  encodings = other94.encodings;
  path_in_schema = other94.path_in_schema;
  codec = other94.codec;
  num_values = other94.num_values;
  total_uncompressed_size = other94.total_uncompressed_size;
  total_compressed_size = other94.total_compressed_size;
  key_value_metadata = other94.key_value_metadata;
  data_page_offset = other94.data_page_offset;
  index_page_offset = other94.index_page_offset;
  dictionary_page_offset = other94.dictionary_page_offset;
  statistics = other94.statistics;
  encoding_stats = other94.encoding_stats;
  __isset = other94.__isset;
}
ColumnMetaData& ColumnMetaData::operator=(const ColumnMetaData& other95) {
  type = other95.type;
  encodings = other95.encodings;
  path_in_schema = other95.path_in_schema;
  codec = other95.codec;
  num_values = other95.num_values;
  total_uncompressed_size = other95.total_uncompressed_size;
  total_compressed_size = other95.total_compressed_size;
  key_value_metadata = other95.key_value_metadata;
  data_page_offset = other95.data_page_offset;
  index_page_offset = other95.index_page_offset;
  dictionary_page_offset = other95.dictionary_page_offset;
  statistics = other95.statistics;
  encoding_stats = other95.encoding_stats;
  __isset = other95.__isset;
  return *this;
}
void ColumnMetaData::printTo(std::ostream& out) const {
  using ::apache::thrift::to_string;
  out << "ColumnMetaData(";
  out << "type=" << to_string(type);
  out << ", " << "encodings=" << to_string(encodings);
  out << ", " << "path_in_schema=" << to_string(path_in_schema);
  out << ", " << "codec=" << to_string(codec);
  out << ", " << "num_values=" << to_string(num_values);
  out << ", " << "total_uncompressed_size=" << to_string(total_uncompressed_size);
  out << ", " << "total_compressed_size=" << to_string(total_compressed_size);
  out << ", " << "key_value_metadata="; (__isset.key_value_metadata ? (out << to_string(key_value_metadata)) : (out << "<null>"));
  out << ", " << "data_page_offset=" << to_string(data_page_offset);
  out << ", " << "index_page_offset="; (__isset.index_page_offset ? (out << to_string(index_page_offset)) : (out << "<null>"));
  out << ", " << "dictionary_page_offset="; (__isset.dictionary_page_offset ? (out << to_string(dictionary_page_offset)) : (out << "<null>"));
  out << ", " << "statistics="; (__isset.statistics ? (out << to_string(statistics)) : (out << "<null>"));
  out << ", " << "encoding_stats="; (__isset.encoding_stats ? (out << to_string(encoding_stats)) : (out << "<null>"));
  out << ")";
}


EncryptionWithFooterKey::~EncryptionWithFooterKey() throw() {
}

std::ostream& operator<<(std::ostream& out, const EncryptionWithFooterKey& obj)
{
  obj.printTo(out);
  return out;
}


uint32_t EncryptionWithFooterKey::read(::apache::thrift::protocol::TProtocol* iprot) {

  ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
  uint32_t xfer = 0;
  std::string fname;
  ::apache::thrift::protocol::TType ftype;
  int16_t fid;

  xfer += iprot->readStructBegin(fname);

  using ::apache::thrift::protocol::TProtocolException;


  while (true)
  {
    xfer += iprot->readFieldBegin(fname, ftype, fid);
    if (ftype == ::apache::thrift::protocol::T_STOP) {
      break;
    }
    xfer += iprot->skip(ftype);
    xfer += iprot->readFieldEnd();
  }

  xfer += iprot->readStructEnd();

  return xfer;
}

uint32_t EncryptionWithFooterKey::write(::apache::thrift::protocol::TProtocol* oprot) const {
  uint32_t xfer = 0;
  ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
  xfer += oprot->writeStructBegin("EncryptionWithFooterKey");

  xfer += oprot->writeFieldStop();
  xfer += oprot->writeStructEnd();
  return xfer;
}

void swap(EncryptionWithFooterKey &a, EncryptionWithFooterKey &b) {
  using ::std::swap;
  (void) a;
  (void) b;
}

EncryptionWithFooterKey::EncryptionWithFooterKey(const EncryptionWithFooterKey& other96) {
  (void) other96;
}
EncryptionWithFooterKey& EncryptionWithFooterKey::operator=(const EncryptionWithFooterKey& other97) {
  (void) other97;
  return *this;
}
void EncryptionWithFooterKey::printTo(std::ostream& out) const {
  using ::apache::thrift::to_string;
  out << "EncryptionWithFooterKey(";
  out << ")";
}


EncryptionWithColumnKey::~EncryptionWithColumnKey() throw() {
}


void EncryptionWithColumnKey::__set_path_in_schema(const std::vector<std::string> & val) {
  this->path_in_schema = val;
}

void EncryptionWithColumnKey::__set_key_metadata(const std::string& val) {
  this->key_metadata = val;
__isset.key_metadata = true;
}
std::ostream& operator<<(std::ostream& out, const EncryptionWithColumnKey& obj)
{
  obj.printTo(out);
  return out;
}


uint32_t EncryptionWithColumnKey::read(::apache::thrift::protocol::TProtocol* iprot) {

  ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
  uint32_t xfer = 0;
  std::string fname;
  ::apache::thrift::protocol::TType ftype;
  int16_t fid;

  xfer += iprot->readStructBegin(fname);

  using ::apache::thrift::protocol::TProtocolException;

  bool isset_path_in_schema = false;

  while (true)
  {
    xfer += iprot->readFieldBegin(fname, ftype, fid);
    if (ftype == ::apache::thrift::protocol::T_STOP) {
      break;
    }
    switch (fid)
    {
      case 1:
        if (ftype == ::apache::thrift::protocol::T_LIST) {
          {
            this->path_in_schema.clear();
            uint32_t _size98;
            ::apache::thrift::protocol::TType _etype101;
            xfer += iprot->readListBegin(_etype101, _size98);
            this->path_in_schema.resize(_size98);
            uint32_t _i102;
            for (_i102 = 0; _i102 < _size98; ++_i102)
            {
              xfer += iprot->readString(this->path_in_schema[_i102]);
            }
            xfer += iprot->readListEnd();
          }
          isset_path_in_schema = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 2:
        if (ftype == ::apache::thrift::protocol::T_STRING) {
          xfer += iprot->readBinary(this->key_metadata);
          this->__isset.key_metadata = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      default:
        xfer += iprot->skip(ftype);
        break;
    }
    xfer += iprot->readFieldEnd();
  }

  xfer += iprot->readStructEnd();

  if (!isset_path_in_schema)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  return xfer;
}

uint32_t EncryptionWithColumnKey::write(::apache::thrift::protocol::TProtocol* oprot) const {
  uint32_t xfer = 0;
  ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
  xfer += oprot->writeStructBegin("EncryptionWithColumnKey");

  xfer += oprot->writeFieldBegin("path_in_schema", ::apache::thrift::protocol::T_LIST, 1);
  {
    xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRING, static_cast<uint32_t>(this->path_in_schema.size()));
    std::vector<std::string> ::const_iterator _iter103;
    for (_iter103 = this->path_in_schema.begin(); _iter103 != this->path_in_schema.end(); ++_iter103)
    {
      xfer += oprot->writeString((*_iter103));
    }
    xfer += oprot->writeListEnd();
  }
  xfer += oprot->writeFieldEnd();

  if (this->__isset.key_metadata) {
    xfer += oprot->writeFieldBegin("key_metadata", ::apache::thrift::protocol::T_STRING, 2);
    xfer += oprot->writeBinary(this->key_metadata);
    xfer += oprot->writeFieldEnd();
  }
  xfer += oprot->writeFieldStop();
  xfer += oprot->writeStructEnd();
  return xfer;
}

void swap(EncryptionWithColumnKey &a, EncryptionWithColumnKey &b) {
  using ::std::swap;
  swap(a.path_in_schema, b.path_in_schema);
  swap(a.key_metadata, b.key_metadata);
  swap(a.__isset, b.__isset);
}

EncryptionWithColumnKey::EncryptionWithColumnKey(const EncryptionWithColumnKey& other104) {
  path_in_schema = other104.path_in_schema;
  key_metadata = other104.key_metadata;
  __isset = other104.__isset;
}
EncryptionWithColumnKey& EncryptionWithColumnKey::operator=(const EncryptionWithColumnKey& other105) {
  path_in_schema = other105.path_in_schema;
  key_metadata = other105.key_metadata;
  __isset = other105.__isset;
  return *this;
}
void EncryptionWithColumnKey::printTo(std::ostream& out) const {
  using ::apache::thrift::to_string;
  out << "EncryptionWithColumnKey(";
  out << "path_in_schema=" << to_string(path_in_schema);
  out << ", " << "key_metadata="; (__isset.key_metadata ? (out << to_string(key_metadata)) : (out << "<null>"));
  out << ")";
}


ColumnCryptoMetaData::~ColumnCryptoMetaData() throw() {
}


void ColumnCryptoMetaData::__set_ENCRYPTION_WITH_FOOTER_KEY(const EncryptionWithFooterKey& val) {
  this->ENCRYPTION_WITH_FOOTER_KEY = val;
__isset.ENCRYPTION_WITH_FOOTER_KEY = true;
}

void ColumnCryptoMetaData::__set_ENCRYPTION_WITH_COLUMN_KEY(const EncryptionWithColumnKey& val) {
  this->ENCRYPTION_WITH_COLUMN_KEY = val;
__isset.ENCRYPTION_WITH_COLUMN_KEY = true;
}
std::ostream& operator<<(std::ostream& out, const ColumnCryptoMetaData& obj)
{
  obj.printTo(out);
  return out;
}


uint32_t ColumnCryptoMetaData::read(::apache::thrift::protocol::TProtocol* iprot) {

  ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
  uint32_t xfer = 0;
  std::string fname;
  ::apache::thrift::protocol::TType ftype;
  int16_t fid;

  xfer += iprot->readStructBegin(fname);

  using ::apache::thrift::protocol::TProtocolException;


  while (true)
  {
    xfer += iprot->readFieldBegin(fname, ftype, fid);
    if (ftype == ::apache::thrift::protocol::T_STOP) {
      break;
    }
    switch (fid)
    {
      case 1:
        if (ftype == ::apache::thrift::protocol::T_STRUCT) {
          xfer += this->ENCRYPTION_WITH_FOOTER_KEY.read(iprot);
          this->__isset.ENCRYPTION_WITH_FOOTER_KEY = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 2:
        if (ftype == ::apache::thrift::protocol::T_STRUCT) {
          xfer += this->ENCRYPTION_WITH_COLUMN_KEY.read(iprot);
          this->__isset.ENCRYPTION_WITH_COLUMN_KEY = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      default:
        xfer += iprot->skip(ftype);
        break;
    }
    xfer += iprot->readFieldEnd();
  }

  xfer += iprot->readStructEnd();

  return xfer;
}

uint32_t ColumnCryptoMetaData::write(::apache::thrift::protocol::TProtocol* oprot) const {
  uint32_t xfer = 0;
  ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
  xfer += oprot->writeStructBegin("ColumnCryptoMetaData");

  if (this->__isset.ENCRYPTION_WITH_FOOTER_KEY) {
    xfer += oprot->writeFieldBegin("ENCRYPTION_WITH_FOOTER_KEY", ::apache::thrift::protocol::T_STRUCT, 1);
    xfer += this->ENCRYPTION_WITH_FOOTER_KEY.write(oprot);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.ENCRYPTION_WITH_COLUMN_KEY) {
    xfer += oprot->writeFieldBegin("ENCRYPTION_WITH_COLUMN_KEY", ::apache::thrift::protocol::T_STRUCT, 2);
    xfer += this->ENCRYPTION_WITH_COLUMN_KEY.write(oprot);
    xfer += oprot->writeFieldEnd();
  }
  xfer += oprot->writeFieldStop();
  xfer += oprot->writeStructEnd();
  return xfer;
}

void swap(ColumnCryptoMetaData &a, ColumnCryptoMetaData &b) {
  using ::std::swap;
  swap(a.ENCRYPTION_WITH_FOOTER_KEY, b.ENCRYPTION_WITH_FOOTER_KEY);
  swap(a.ENCRYPTION_WITH_COLUMN_KEY, b.ENCRYPTION_WITH_COLUMN_KEY);
  swap(a.__isset, b.__isset);
}

ColumnCryptoMetaData::ColumnCryptoMetaData(const ColumnCryptoMetaData& other106) {
  ENCRYPTION_WITH_FOOTER_KEY = other106.ENCRYPTION_WITH_FOOTER_KEY;
  ENCRYPTION_WITH_COLUMN_KEY = other106.ENCRYPTION_WITH_COLUMN_KEY;
  __isset = other106.__isset;
}
ColumnCryptoMetaData& ColumnCryptoMetaData::operator=(const ColumnCryptoMetaData& other107) {
  ENCRYPTION_WITH_FOOTER_KEY = other107.ENCRYPTION_WITH_FOOTER_KEY;
  ENCRYPTION_WITH_COLUMN_KEY = other107.ENCRYPTION_WITH_COLUMN_KEY;
  __isset = other107.__isset;
  return *this;
}
void ColumnCryptoMetaData::printTo(std::ostream& out) const {
  using ::apache::thrift::to_string;
  out << "ColumnCryptoMetaData(";
  out << "ENCRYPTION_WITH_FOOTER_KEY="; (__isset.ENCRYPTION_WITH_FOOTER_KEY ? (out << to_string(ENCRYPTION_WITH_FOOTER_KEY)) : (out << "<null>"));
  out << ", " << "ENCRYPTION_WITH_COLUMN_KEY="; (__isset.ENCRYPTION_WITH_COLUMN_KEY ? (out << to_string(ENCRYPTION_WITH_COLUMN_KEY)) : (out << "<null>"));
  out << ")";
}


ColumnChunk::~ColumnChunk() throw() {
}


void ColumnChunk::__set_file_path(const std::string& val) {
  this->file_path = val;
__isset.file_path = true;
}

void ColumnChunk::__set_file_offset(const int64_t val) {
  this->file_offset = val;
}

void ColumnChunk::__set_meta_data(const ColumnMetaData& val) {
  this->meta_data = val;
__isset.meta_data = true;
}

void ColumnChunk::__set_offset_index_offset(const int64_t val) {
  this->offset_index_offset = val;
__isset.offset_index_offset = true;
}

void ColumnChunk::__set_offset_index_length(const int32_t val) {
  this->offset_index_length = val;
__isset.offset_index_length = true;
}

void ColumnChunk::__set_column_index_offset(const int64_t val) {
  this->column_index_offset = val;
__isset.column_index_offset = true;
}

void ColumnChunk::__set_column_index_length(const int32_t val) {
  this->column_index_length = val;
__isset.column_index_length = true;
}

void ColumnChunk::__set_crypto_metadata(const ColumnCryptoMetaData& val) {
  this->crypto_metadata = val;
__isset.crypto_metadata = true;
}

void ColumnChunk::__set_encrypted_column_metadata(const std::string& val) {
  this->encrypted_column_metadata = val;
__isset.encrypted_column_metadata = true;
}
std::ostream& operator<<(std::ostream& out, const ColumnChunk& obj)
{
  obj.printTo(out);
  return out;
}


uint32_t ColumnChunk::read(::apache::thrift::protocol::TProtocol* iprot) {

  ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
  uint32_t xfer = 0;
  std::string fname;
  ::apache::thrift::protocol::TType ftype;
  int16_t fid;

  xfer += iprot->readStructBegin(fname);

  using ::apache::thrift::protocol::TProtocolException;

  bool isset_file_offset = false;

  while (true)
  {
    xfer += iprot->readFieldBegin(fname, ftype, fid);
    if (ftype == ::apache::thrift::protocol::T_STOP) {
      break;
    }
    switch (fid)
    {
      case 1:
        if (ftype == ::apache::thrift::protocol::T_STRING) {
          xfer += iprot->readString(this->file_path);
          this->__isset.file_path = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 2:
        if (ftype == ::apache::thrift::protocol::T_I64) {
          xfer += iprot->readI64(this->file_offset);
          isset_file_offset = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 3:
        if (ftype == ::apache::thrift::protocol::T_STRUCT) {
          xfer += this->meta_data.read(iprot);
          this->__isset.meta_data = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 4:
        if (ftype == ::apache::thrift::protocol::T_I64) {
          xfer += iprot->readI64(this->offset_index_offset);
          this->__isset.offset_index_offset = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 5:
        if (ftype == ::apache::thrift::protocol::T_I32) {
          xfer += iprot->readI32(this->offset_index_length);
          this->__isset.offset_index_length = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 6:
        if (ftype == ::apache::thrift::protocol::T_I64) {
          xfer += iprot->readI64(this->column_index_offset);
          this->__isset.column_index_offset = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 7:
        if (ftype == ::apache::thrift::protocol::T_I32) {
          xfer += iprot->readI32(this->column_index_length);
          this->__isset.column_index_length = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 8:
        if (ftype == ::apache::thrift::protocol::T_STRUCT) {
          xfer += this->crypto_metadata.read(iprot);
          this->__isset.crypto_metadata = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 9:
        if (ftype == ::apache::thrift::protocol::T_STRING) {
          xfer += iprot->readBinary(this->encrypted_column_metadata);
          this->__isset.encrypted_column_metadata = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      default:
        xfer += iprot->skip(ftype);
        break;
    }
    xfer += iprot->readFieldEnd();
  }

  xfer += iprot->readStructEnd();

  if (!isset_file_offset)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  return xfer;
}

uint32_t ColumnChunk::write(::apache::thrift::protocol::TProtocol* oprot) const {
  uint32_t xfer = 0;
  ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
  xfer += oprot->writeStructBegin("ColumnChunk");

  if (this->__isset.file_path) {
    xfer += oprot->writeFieldBegin("file_path", ::apache::thrift::protocol::T_STRING, 1);
    xfer += oprot->writeString(this->file_path);
    xfer += oprot->writeFieldEnd();
  }
  xfer += oprot->writeFieldBegin("file_offset", ::apache::thrift::protocol::T_I64, 2);
  xfer += oprot->writeI64(this->file_offset);
  xfer += oprot->writeFieldEnd();

  if (this->__isset.meta_data) {
    xfer += oprot->writeFieldBegin("meta_data", ::apache::thrift::protocol::T_STRUCT, 3);
    xfer += this->meta_data.write(oprot);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.offset_index_offset) {
    xfer += oprot->writeFieldBegin("offset_index_offset", ::apache::thrift::protocol::T_I64, 4);
    xfer += oprot->writeI64(this->offset_index_offset);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.offset_index_length) {
    xfer += oprot->writeFieldBegin("offset_index_length", ::apache::thrift::protocol::T_I32, 5);
    xfer += oprot->writeI32(this->offset_index_length);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.column_index_offset) {
    xfer += oprot->writeFieldBegin("column_index_offset", ::apache::thrift::protocol::T_I64, 6);
    xfer += oprot->writeI64(this->column_index_offset);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.column_index_length) {
    xfer += oprot->writeFieldBegin("column_index_length", ::apache::thrift::protocol::T_I32, 7);
    xfer += oprot->writeI32(this->column_index_length);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.crypto_metadata) {
    xfer += oprot->writeFieldBegin("crypto_metadata", ::apache::thrift::protocol::T_STRUCT, 8);
    xfer += this->crypto_metadata.write(oprot);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.encrypted_column_metadata) {
    xfer += oprot->writeFieldBegin("encrypted_column_metadata", ::apache::thrift::protocol::T_STRING, 9);
    xfer += oprot->writeBinary(this->encrypted_column_metadata);
    xfer += oprot->writeFieldEnd();
  }
  xfer += oprot->writeFieldStop();
  xfer += oprot->writeStructEnd();
  return xfer;
}

void swap(ColumnChunk &a, ColumnChunk &b) {
  using ::std::swap;
  swap(a.file_path, b.file_path);
  swap(a.file_offset, b.file_offset);
  swap(a.meta_data, b.meta_data);
  swap(a.offset_index_offset, b.offset_index_offset);
  swap(a.offset_index_length, b.offset_index_length);
  swap(a.column_index_offset, b.column_index_offset);
  swap(a.column_index_length, b.column_index_length);
  swap(a.crypto_metadata, b.crypto_metadata);
  swap(a.encrypted_column_metadata, b.encrypted_column_metadata);
  swap(a.__isset, b.__isset);
}

ColumnChunk::ColumnChunk(const ColumnChunk& other108) {
  file_path = other108.file_path;
  file_offset = other108.file_offset;
  meta_data = other108.meta_data;
  offset_index_offset = other108.offset_index_offset;
  offset_index_length = other108.offset_index_length;
  column_index_offset = other108.column_index_offset;
  column_index_length = other108.column_index_length;
  crypto_metadata = other108.crypto_metadata;
  encrypted_column_metadata = other108.encrypted_column_metadata;
  __isset = other108.__isset;
}
ColumnChunk& ColumnChunk::operator=(const ColumnChunk& other109) {
  file_path = other109.file_path;
  file_offset = other109.file_offset;
  meta_data = other109.meta_data;
  offset_index_offset = other109.offset_index_offset;
  offset_index_length = other109.offset_index_length;
  column_index_offset = other109.column_index_offset;
  column_index_length = other109.column_index_length;
  crypto_metadata = other109.crypto_metadata;
  encrypted_column_metadata = other109.encrypted_column_metadata;
  __isset = other109.__isset;
  return *this;
}
void ColumnChunk::printTo(std::ostream& out) const {
  using ::apache::thrift::to_string;
  out << "ColumnChunk(";
  out << "file_path="; (__isset.file_path ? (out << to_string(file_path)) : (out << "<null>"));
  out << ", " << "file_offset=" << to_string(file_offset);
  out << ", " << "meta_data="; (__isset.meta_data ? (out << to_string(meta_data)) : (out << "<null>"));
  out << ", " << "offset_index_offset="; (__isset.offset_index_offset ? (out << to_string(offset_index_offset)) : (out << "<null>"));
  out << ", " << "offset_index_length="; (__isset.offset_index_length ? (out << to_string(offset_index_length)) : (out << "<null>"));
  out << ", " << "column_index_offset="; (__isset.column_index_offset ? (out << to_string(column_index_offset)) : (out << "<null>"));
  out << ", " << "column_index_length="; (__isset.column_index_length ? (out << to_string(column_index_length)) : (out << "<null>"));
  out << ", " << "crypto_metadata="; (__isset.crypto_metadata ? (out << to_string(crypto_metadata)) : (out << "<null>"));
  out << ", " << "encrypted_column_metadata="; (__isset.encrypted_column_metadata ? (out << to_string(encrypted_column_metadata)) : (out << "<null>"));
  out << ")";
}


RowGroup::~RowGroup() throw() {
}


void RowGroup::__set_columns(const std::vector<ColumnChunk> & val) {
  this->columns = val;
}

void RowGroup::__set_total_byte_size(const int64_t val) {
  this->total_byte_size = val;
}

void RowGroup::__set_num_rows(const int64_t val) {
  this->num_rows = val;
}

void RowGroup::__set_sorting_columns(const std::vector<SortingColumn> & val) {
  this->sorting_columns = val;
__isset.sorting_columns = true;
}

void RowGroup::__set_file_offset(const int64_t val) {
  this->file_offset = val;
__isset.file_offset = true;
}

void RowGroup::__set_total_compressed_size(const int64_t val) {
  this->total_compressed_size = val;
__isset.total_compressed_size = true;
}

void RowGroup::__set_ordinal(const int16_t val) {
  this->ordinal = val;
__isset.ordinal = true;
}
std::ostream& operator<<(std::ostream& out, const RowGroup& obj)
{
  obj.printTo(out);
  return out;
}


uint32_t RowGroup::read(::apache::thrift::protocol::TProtocol* iprot) {

  ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
  uint32_t xfer = 0;
  std::string fname;
  ::apache::thrift::protocol::TType ftype;
  int16_t fid;

  xfer += iprot->readStructBegin(fname);

  using ::apache::thrift::protocol::TProtocolException;

  bool isset_columns = false;
  bool isset_total_byte_size = false;
  bool isset_num_rows = false;

  while (true)
  {
    xfer += iprot->readFieldBegin(fname, ftype, fid);
    if (ftype == ::apache::thrift::protocol::T_STOP) {
      break;
    }
    switch (fid)
    {
      case 1:
        if (ftype == ::apache::thrift::protocol::T_LIST) {
          {
            this->columns.clear();
            uint32_t _size110;
            ::apache::thrift::protocol::TType _etype113;
            xfer += iprot->readListBegin(_etype113, _size110);
            this->columns.resize(_size110);
            uint32_t _i114;
            for (_i114 = 0; _i114 < _size110; ++_i114)
            {
              xfer += this->columns[_i114].read(iprot);
            }
            xfer += iprot->readListEnd();
          }
          isset_columns = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 2:
        if (ftype == ::apache::thrift::protocol::T_I64) {
          xfer += iprot->readI64(this->total_byte_size);
          isset_total_byte_size = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 3:
        if (ftype == ::apache::thrift::protocol::T_I64) {
          xfer += iprot->readI64(this->num_rows);
          isset_num_rows = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 4:
        if (ftype == ::apache::thrift::protocol::T_LIST) {
          {
            this->sorting_columns.clear();
            uint32_t _size115;
            ::apache::thrift::protocol::TType _etype118;
            xfer += iprot->readListBegin(_etype118, _size115);
            this->sorting_columns.resize(_size115);
            uint32_t _i119;
            for (_i119 = 0; _i119 < _size115; ++_i119)
            {
              xfer += this->sorting_columns[_i119].read(iprot);
            }
            xfer += iprot->readListEnd();
          }
          this->__isset.sorting_columns = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 5:
        if (ftype == ::apache::thrift::protocol::T_I64) {
          xfer += iprot->readI64(this->file_offset);
          this->__isset.file_offset = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 6:
        if (ftype == ::apache::thrift::protocol::T_I64) {
          xfer += iprot->readI64(this->total_compressed_size);
          this->__isset.total_compressed_size = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 7:
        if (ftype == ::apache::thrift::protocol::T_I16) {
          xfer += iprot->readI16(this->ordinal);
          this->__isset.ordinal = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      default:
        xfer += iprot->skip(ftype);
        break;
    }
    xfer += iprot->readFieldEnd();
  }

  xfer += iprot->readStructEnd();

  if (!isset_columns)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  if (!isset_total_byte_size)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  if (!isset_num_rows)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  return xfer;
}

uint32_t RowGroup::write(::apache::thrift::protocol::TProtocol* oprot) const {
  uint32_t xfer = 0;
  ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
  xfer += oprot->writeStructBegin("RowGroup");

  xfer += oprot->writeFieldBegin("columns", ::apache::thrift::protocol::T_LIST, 1);
  {
    xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, static_cast<uint32_t>(this->columns.size()));
    std::vector<ColumnChunk> ::const_iterator _iter120;
    for (_iter120 = this->columns.begin(); _iter120 != this->columns.end(); ++_iter120)
    {
      xfer += (*_iter120).write(oprot);
    }
    xfer += oprot->writeListEnd();
  }
  xfer += oprot->writeFieldEnd();

  xfer += oprot->writeFieldBegin("total_byte_size", ::apache::thrift::protocol::T_I64, 2);
  xfer += oprot->writeI64(this->total_byte_size);
  xfer += oprot->writeFieldEnd();

  xfer += oprot->writeFieldBegin("num_rows", ::apache::thrift::protocol::T_I64, 3);
  xfer += oprot->writeI64(this->num_rows);
  xfer += oprot->writeFieldEnd();

  if (this->__isset.sorting_columns) {
    xfer += oprot->writeFieldBegin("sorting_columns", ::apache::thrift::protocol::T_LIST, 4);
    {
      xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, static_cast<uint32_t>(this->sorting_columns.size()));
      std::vector<SortingColumn> ::const_iterator _iter121;
      for (_iter121 = this->sorting_columns.begin(); _iter121 != this->sorting_columns.end(); ++_iter121)
      {
        xfer += (*_iter121).write(oprot);
      }
      xfer += oprot->writeListEnd();
    }
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.file_offset) {
    xfer += oprot->writeFieldBegin("file_offset", ::apache::thrift::protocol::T_I64, 5);
    xfer += oprot->writeI64(this->file_offset);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.total_compressed_size) {
    xfer += oprot->writeFieldBegin("total_compressed_size", ::apache::thrift::protocol::T_I64, 6);
    xfer += oprot->writeI64(this->total_compressed_size);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.ordinal) {
    xfer += oprot->writeFieldBegin("ordinal", ::apache::thrift::protocol::T_I16, 7);
    xfer += oprot->writeI16(this->ordinal);
    xfer += oprot->writeFieldEnd();
  }
  xfer += oprot->writeFieldStop();
  xfer += oprot->writeStructEnd();
  return xfer;
}

void swap(RowGroup &a, RowGroup &b) {
  using ::std::swap;
  swap(a.columns, b.columns);
  swap(a.total_byte_size, b.total_byte_size);
  swap(a.num_rows, b.num_rows);
  swap(a.sorting_columns, b.sorting_columns);
  swap(a.file_offset, b.file_offset);
  swap(a.total_compressed_size, b.total_compressed_size);
  swap(a.ordinal, b.ordinal);
  swap(a.__isset, b.__isset);
}

RowGroup::RowGroup(const RowGroup& other122) {
  columns = other122.columns;
  total_byte_size = other122.total_byte_size;
  num_rows = other122.num_rows;
  sorting_columns = other122.sorting_columns;
  file_offset = other122.file_offset;
  total_compressed_size = other122.total_compressed_size;
  ordinal = other122.ordinal;
  __isset = other122.__isset;
}
RowGroup& RowGroup::operator=(const RowGroup& other123) {
  columns = other123.columns;
  total_byte_size = other123.total_byte_size;
  num_rows = other123.num_rows;
  sorting_columns = other123.sorting_columns;
  file_offset = other123.file_offset;
  total_compressed_size = other123.total_compressed_size;
  ordinal = other123.ordinal;
  __isset = other123.__isset;
  return *this;
}
void RowGroup::printTo(std::ostream& out) const {
  using ::apache::thrift::to_string;
  out << "RowGroup(";
  out << "columns=" << to_string(columns);
  out << ", " << "total_byte_size=" << to_string(total_byte_size);
  out << ", " << "num_rows=" << to_string(num_rows);
  out << ", " << "sorting_columns="; (__isset.sorting_columns ? (out << to_string(sorting_columns)) : (out << "<null>"));
  out << ", " << "file_offset="; (__isset.file_offset ? (out << to_string(file_offset)) : (out << "<null>"));
  out << ", " << "total_compressed_size="; (__isset.total_compressed_size ? (out << to_string(total_compressed_size)) : (out << "<null>"));
  out << ", " << "ordinal="; (__isset.ordinal ? (out << to_string(ordinal)) : (out << "<null>"));
  out << ")";
}


TypeDefinedOrder::~TypeDefinedOrder() throw() {
}

std::ostream& operator<<(std::ostream& out, const TypeDefinedOrder& obj)
{
  obj.printTo(out);
  return out;
}


uint32_t TypeDefinedOrder::read(::apache::thrift::protocol::TProtocol* iprot) {

  ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
  uint32_t xfer = 0;
  std::string fname;
  ::apache::thrift::protocol::TType ftype;
  int16_t fid;

  xfer += iprot->readStructBegin(fname);

  using ::apache::thrift::protocol::TProtocolException;


  while (true)
  {
    xfer += iprot->readFieldBegin(fname, ftype, fid);
    if (ftype == ::apache::thrift::protocol::T_STOP) {
      break;
    }
    xfer += iprot->skip(ftype);
    xfer += iprot->readFieldEnd();
  }

  xfer += iprot->readStructEnd();

  return xfer;
}

uint32_t TypeDefinedOrder::write(::apache::thrift::protocol::TProtocol* oprot) const {
  uint32_t xfer = 0;
  ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
  xfer += oprot->writeStructBegin("TypeDefinedOrder");

  xfer += oprot->writeFieldStop();
  xfer += oprot->writeStructEnd();
  return xfer;
}

void swap(TypeDefinedOrder &a, TypeDefinedOrder &b) {
  using ::std::swap;
  (void) a;
  (void) b;
}

TypeDefinedOrder::TypeDefinedOrder(const TypeDefinedOrder& other124) {
  (void) other124;
}
TypeDefinedOrder& TypeDefinedOrder::operator=(const TypeDefinedOrder& other125) {
  (void) other125;
  return *this;
}
void TypeDefinedOrder::printTo(std::ostream& out) const {
  using ::apache::thrift::to_string;
  out << "TypeDefinedOrder(";
  out << ")";
}


ColumnOrder::~ColumnOrder() throw() {
}


void ColumnOrder::__set_TYPE_ORDER(const TypeDefinedOrder& val) {
  this->TYPE_ORDER = val;
__isset.TYPE_ORDER = true;
}
std::ostream& operator<<(std::ostream& out, const ColumnOrder& obj)
{
  obj.printTo(out);
  return out;
}


uint32_t ColumnOrder::read(::apache::thrift::protocol::TProtocol* iprot) {

  ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
  uint32_t xfer = 0;
  std::string fname;
  ::apache::thrift::protocol::TType ftype;
  int16_t fid;

  xfer += iprot->readStructBegin(fname);

  using ::apache::thrift::protocol::TProtocolException;


  while (true)
  {
    xfer += iprot->readFieldBegin(fname, ftype, fid);
    if (ftype == ::apache::thrift::protocol::T_STOP) {
      break;
    }
    switch (fid)
    {
      case 1:
        if (ftype == ::apache::thrift::protocol::T_STRUCT) {
          xfer += this->TYPE_ORDER.read(iprot);
          this->__isset.TYPE_ORDER = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      default:
        xfer += iprot->skip(ftype);
        break;
    }
    xfer += iprot->readFieldEnd();
  }

  xfer += iprot->readStructEnd();

  return xfer;
}

uint32_t ColumnOrder::write(::apache::thrift::protocol::TProtocol* oprot) const {
  uint32_t xfer = 0;
  ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
  xfer += oprot->writeStructBegin("ColumnOrder");

  if (this->__isset.TYPE_ORDER) {
    xfer += oprot->writeFieldBegin("TYPE_ORDER", ::apache::thrift::protocol::T_STRUCT, 1);
    xfer += this->TYPE_ORDER.write(oprot);
    xfer += oprot->writeFieldEnd();
  }
  xfer += oprot->writeFieldStop();
  xfer += oprot->writeStructEnd();
  return xfer;
}

void swap(ColumnOrder &a, ColumnOrder &b) {
  using ::std::swap;
  swap(a.TYPE_ORDER, b.TYPE_ORDER);
  swap(a.__isset, b.__isset);
}

ColumnOrder::ColumnOrder(const ColumnOrder& other126) {
  TYPE_ORDER = other126.TYPE_ORDER;
  __isset = other126.__isset;
}
ColumnOrder& ColumnOrder::operator=(const ColumnOrder& other127) {
  TYPE_ORDER = other127.TYPE_ORDER;
  __isset = other127.__isset;
  return *this;
}
void ColumnOrder::printTo(std::ostream& out) const {
  using ::apache::thrift::to_string;
  out << "ColumnOrder(";
  out << "TYPE_ORDER="; (__isset.TYPE_ORDER ? (out << to_string(TYPE_ORDER)) : (out << "<null>"));
  out << ")";
}


PageLocation::~PageLocation() throw() {
}


void PageLocation::__set_offset(const int64_t val) {
  this->offset = val;
}

void PageLocation::__set_compressed_page_size(const int32_t val) {
  this->compressed_page_size = val;
}

void PageLocation::__set_first_row_index(const int64_t val) {
  this->first_row_index = val;
}
std::ostream& operator<<(std::ostream& out, const PageLocation& obj)
{
  obj.printTo(out);
  return out;
}


uint32_t PageLocation::read(::apache::thrift::protocol::TProtocol* iprot) {

  ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
  uint32_t xfer = 0;
  std::string fname;
  ::apache::thrift::protocol::TType ftype;
  int16_t fid;

  xfer += iprot->readStructBegin(fname);

  using ::apache::thrift::protocol::TProtocolException;

  bool isset_offset = false;
  bool isset_compressed_page_size = false;
  bool isset_first_row_index = false;

  while (true)
  {
    xfer += iprot->readFieldBegin(fname, ftype, fid);
    if (ftype == ::apache::thrift::protocol::T_STOP) {
      break;
    }
    switch (fid)
    {
      case 1:
        if (ftype == ::apache::thrift::protocol::T_I64) {
          xfer += iprot->readI64(this->offset);
          isset_offset = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 2:
        if (ftype == ::apache::thrift::protocol::T_I32) {
          xfer += iprot->readI32(this->compressed_page_size);
          isset_compressed_page_size = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 3:
        if (ftype == ::apache::thrift::protocol::T_I64) {
          xfer += iprot->readI64(this->first_row_index);
          isset_first_row_index = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      default:
        xfer += iprot->skip(ftype);
        break;
    }
    xfer += iprot->readFieldEnd();
  }

  xfer += iprot->readStructEnd();

  if (!isset_offset)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  if (!isset_compressed_page_size)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  if (!isset_first_row_index)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  return xfer;
}

uint32_t PageLocation::write(::apache::thrift::protocol::TProtocol* oprot) const {
  uint32_t xfer = 0;
  ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
  xfer += oprot->writeStructBegin("PageLocation");

  xfer += oprot->writeFieldBegin("offset", ::apache::thrift::protocol::T_I64, 1);
  xfer += oprot->writeI64(this->offset);
  xfer += oprot->writeFieldEnd();

  xfer += oprot->writeFieldBegin("compressed_page_size", ::apache::thrift::protocol::T_I32, 2);
  xfer += oprot->writeI32(this->compressed_page_size);
  xfer += oprot->writeFieldEnd();

  xfer += oprot->writeFieldBegin("first_row_index", ::apache::thrift::protocol::T_I64, 3);
  xfer += oprot->writeI64(this->first_row_index);
  xfer += oprot->writeFieldEnd();

  xfer += oprot->writeFieldStop();
  xfer += oprot->writeStructEnd();
  return xfer;
}

void swap(PageLocation &a, PageLocation &b) {
  using ::std::swap;
  swap(a.offset, b.offset);
  swap(a.compressed_page_size, b.compressed_page_size);
  swap(a.first_row_index, b.first_row_index);
}

PageLocation::PageLocation(const PageLocation& other128) {
  offset = other128.offset;
  compressed_page_size = other128.compressed_page_size;
  first_row_index = other128.first_row_index;
}
PageLocation& PageLocation::operator=(const PageLocation& other129) {
  offset = other129.offset;
  compressed_page_size = other129.compressed_page_size;
  first_row_index = other129.first_row_index;
  return *this;
}
void PageLocation::printTo(std::ostream& out) const {
  using ::apache::thrift::to_string;
  out << "PageLocation(";
  out << "offset=" << to_string(offset);
  out << ", " << "compressed_page_size=" << to_string(compressed_page_size);
  out << ", " << "first_row_index=" << to_string(first_row_index);
  out << ")";
}


OffsetIndex::~OffsetIndex() throw() {
}


void OffsetIndex::__set_page_locations(const std::vector<PageLocation> & val) {
  this->page_locations = val;
}
std::ostream& operator<<(std::ostream& out, const OffsetIndex& obj)
{
  obj.printTo(out);
  return out;
}


uint32_t OffsetIndex::read(::apache::thrift::protocol::TProtocol* iprot) {

  ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
  uint32_t xfer = 0;
  std::string fname;
  ::apache::thrift::protocol::TType ftype;
  int16_t fid;

  xfer += iprot->readStructBegin(fname);

  using ::apache::thrift::protocol::TProtocolException;

  bool isset_page_locations = false;

  while (true)
  {
    xfer += iprot->readFieldBegin(fname, ftype, fid);
    if (ftype == ::apache::thrift::protocol::T_STOP) {
      break;
    }
    switch (fid)
    {
      case 1:
        if (ftype == ::apache::thrift::protocol::T_LIST) {
          {
            this->page_locations.clear();
            uint32_t _size130;
            ::apache::thrift::protocol::TType _etype133;
            xfer += iprot->readListBegin(_etype133, _size130);
            this->page_locations.resize(_size130);
            uint32_t _i134;
            for (_i134 = 0; _i134 < _size130; ++_i134)
            {
              xfer += this->page_locations[_i134].read(iprot);
            }
            xfer += iprot->readListEnd();
          }
          isset_page_locations = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      default:
        xfer += iprot->skip(ftype);
        break;
    }
    xfer += iprot->readFieldEnd();
  }

  xfer += iprot->readStructEnd();

  if (!isset_page_locations)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  return xfer;
}

uint32_t OffsetIndex::write(::apache::thrift::protocol::TProtocol* oprot) const {
  uint32_t xfer = 0;
  ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
  xfer += oprot->writeStructBegin("OffsetIndex");

  xfer += oprot->writeFieldBegin("page_locations", ::apache::thrift::protocol::T_LIST, 1);
  {
    xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, static_cast<uint32_t>(this->page_locations.size()));
    std::vector<PageLocation> ::const_iterator _iter135;
    for (_iter135 = this->page_locations.begin(); _iter135 != this->page_locations.end(); ++_iter135)
    {
      xfer += (*_iter135).write(oprot);
    }
    xfer += oprot->writeListEnd();
  }
  xfer += oprot->writeFieldEnd();

  xfer += oprot->writeFieldStop();
  xfer += oprot->writeStructEnd();
  return xfer;
}

void swap(OffsetIndex &a, OffsetIndex &b) {
  using ::std::swap;
  swap(a.page_locations, b.page_locations);
}

OffsetIndex::OffsetIndex(const OffsetIndex& other136) {
  page_locations = other136.page_locations;
}
OffsetIndex& OffsetIndex::operator=(const OffsetIndex& other137) {
  page_locations = other137.page_locations;
  return *this;
}
void OffsetIndex::printTo(std::ostream& out) const {
  using ::apache::thrift::to_string;
  out << "OffsetIndex(";
  out << "page_locations=" << to_string(page_locations);
  out << ")";
}


ColumnIndex::~ColumnIndex() throw() {
}


void ColumnIndex::__set_null_pages(const std::vector<bool> & val) {
  this->null_pages = val;
}

void ColumnIndex::__set_min_values(const std::vector<std::string> & val) {
  this->min_values = val;
}

void ColumnIndex::__set_max_values(const std::vector<std::string> & val) {
  this->max_values = val;
}

void ColumnIndex::__set_boundary_order(const BoundaryOrder::type val) {
  this->boundary_order = val;
}

void ColumnIndex::__set_null_counts(const std::vector<int64_t> & val) {
  this->null_counts = val;
__isset.null_counts = true;
}
std::ostream& operator<<(std::ostream& out, const ColumnIndex& obj)
{
  obj.printTo(out);
  return out;
}


uint32_t ColumnIndex::read(::apache::thrift::protocol::TProtocol* iprot) {

  ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
  uint32_t xfer = 0;
  std::string fname;
  ::apache::thrift::protocol::TType ftype;
  int16_t fid;

  xfer += iprot->readStructBegin(fname);

  using ::apache::thrift::protocol::TProtocolException;

  bool isset_null_pages = false;
  bool isset_min_values = false;
  bool isset_max_values = false;
  bool isset_boundary_order = false;

  while (true)
  {
    xfer += iprot->readFieldBegin(fname, ftype, fid);
    if (ftype == ::apache::thrift::protocol::T_STOP) {
      break;
    }
    switch (fid)
    {
      case 1:
        if (ftype == ::apache::thrift::protocol::T_LIST) {
          {
            this->null_pages.clear();
            uint32_t _size138;
            ::apache::thrift::protocol::TType _etype141;
            xfer += iprot->readListBegin(_etype141, _size138);
            this->null_pages.resize(_size138);
            uint32_t _i142;
            for (_i142 = 0; _i142 < _size138; ++_i142)
            {
              xfer += iprot->readBool(this->null_pages[_i142]);
            }
            xfer += iprot->readListEnd();
          }
          isset_null_pages = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 2:
        if (ftype == ::apache::thrift::protocol::T_LIST) {
          {
            this->min_values.clear();
            uint32_t _size143;
            ::apache::thrift::protocol::TType _etype146;
            xfer += iprot->readListBegin(_etype146, _size143);
            this->min_values.resize(_size143);
            uint32_t _i147;
            for (_i147 = 0; _i147 < _size143; ++_i147)
            {
              xfer += iprot->readBinary(this->min_values[_i147]);
            }
            xfer += iprot->readListEnd();
          }
          isset_min_values = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 3:
        if (ftype == ::apache::thrift::protocol::T_LIST) {
          {
            this->max_values.clear();
            uint32_t _size148;
            ::apache::thrift::protocol::TType _etype151;
            xfer += iprot->readListBegin(_etype151, _size148);
            this->max_values.resize(_size148);
            uint32_t _i152;
            for (_i152 = 0; _i152 < _size148; ++_i152)
            {
              xfer += iprot->readBinary(this->max_values[_i152]);
            }
            xfer += iprot->readListEnd();
          }
          isset_max_values = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 4:
        if (ftype == ::apache::thrift::protocol::T_I32) {
          int32_t ecast153;
          xfer += iprot->readI32(ecast153);
          this->boundary_order = (BoundaryOrder::type)ecast153;
          isset_boundary_order = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 5:
        if (ftype == ::apache::thrift::protocol::T_LIST) {
          {
            this->null_counts.clear();
            uint32_t _size154;
            ::apache::thrift::protocol::TType _etype157;
            xfer += iprot->readListBegin(_etype157, _size154);
            this->null_counts.resize(_size154);
            uint32_t _i158;
            for (_i158 = 0; _i158 < _size154; ++_i158)
            {
              xfer += iprot->readI64(this->null_counts[_i158]);
            }
            xfer += iprot->readListEnd();
          }
          this->__isset.null_counts = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      default:
        xfer += iprot->skip(ftype);
        break;
    }
    xfer += iprot->readFieldEnd();
  }

  xfer += iprot->readStructEnd();

  if (!isset_null_pages)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  if (!isset_min_values)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  if (!isset_max_values)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  if (!isset_boundary_order)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  return xfer;
}

uint32_t ColumnIndex::write(::apache::thrift::protocol::TProtocol* oprot) const {
  uint32_t xfer = 0;
  ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
  xfer += oprot->writeStructBegin("ColumnIndex");

  xfer += oprot->writeFieldBegin("null_pages", ::apache::thrift::protocol::T_LIST, 1);
  {
    xfer += oprot->writeListBegin(::apache::thrift::protocol::T_BOOL, static_cast<uint32_t>(this->null_pages.size()));
    std::vector<bool> ::const_iterator _iter159;
    for (_iter159 = this->null_pages.begin(); _iter159 != this->null_pages.end(); ++_iter159)
    {
      xfer += oprot->writeBool((*_iter159));
    }
    xfer += oprot->writeListEnd();
  }
  xfer += oprot->writeFieldEnd();

  xfer += oprot->writeFieldBegin("min_values", ::apache::thrift::protocol::T_LIST, 2);
  {
    xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRING, static_cast<uint32_t>(this->min_values.size()));
    std::vector<std::string> ::const_iterator _iter160;
    for (_iter160 = this->min_values.begin(); _iter160 != this->min_values.end(); ++_iter160)
    {
      xfer += oprot->writeBinary((*_iter160));
    }
    xfer += oprot->writeListEnd();
  }
  xfer += oprot->writeFieldEnd();

  xfer += oprot->writeFieldBegin("max_values", ::apache::thrift::protocol::T_LIST, 3);
  {
    xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRING, static_cast<uint32_t>(this->max_values.size()));
    std::vector<std::string> ::const_iterator _iter161;
    for (_iter161 = this->max_values.begin(); _iter161 != this->max_values.end(); ++_iter161)
    {
      xfer += oprot->writeBinary((*_iter161));
    }
    xfer += oprot->writeListEnd();
  }
  xfer += oprot->writeFieldEnd();

  xfer += oprot->writeFieldBegin("boundary_order", ::apache::thrift::protocol::T_I32, 4);
  xfer += oprot->writeI32((int32_t)this->boundary_order);
  xfer += oprot->writeFieldEnd();

  if (this->__isset.null_counts) {
    xfer += oprot->writeFieldBegin("null_counts", ::apache::thrift::protocol::T_LIST, 5);
    {
      xfer += oprot->writeListBegin(::apache::thrift::protocol::T_I64, static_cast<uint32_t>(this->null_counts.size()));
      std::vector<int64_t> ::const_iterator _iter162;
      for (_iter162 = this->null_counts.begin(); _iter162 != this->null_counts.end(); ++_iter162)
      {
        xfer += oprot->writeI64((*_iter162));
      }
      xfer += oprot->writeListEnd();
    }
    xfer += oprot->writeFieldEnd();
  }
  xfer += oprot->writeFieldStop();
  xfer += oprot->writeStructEnd();
  return xfer;
}

void swap(ColumnIndex &a, ColumnIndex &b) {
  using ::std::swap;
  swap(a.null_pages, b.null_pages);
  swap(a.min_values, b.min_values);
  swap(a.max_values, b.max_values);
  swap(a.boundary_order, b.boundary_order);
  swap(a.null_counts, b.null_counts);
  swap(a.__isset, b.__isset);
}

ColumnIndex::ColumnIndex(const ColumnIndex& other163) {
  null_pages = other163.null_pages;
  min_values = other163.min_values;
  max_values = other163.max_values;
  boundary_order = other163.boundary_order;
  null_counts = other163.null_counts;
  __isset = other163.__isset;
}
ColumnIndex& ColumnIndex::operator=(const ColumnIndex& other164) {
  null_pages = other164.null_pages;
  min_values = other164.min_values;
  max_values = other164.max_values;
  boundary_order = other164.boundary_order;
  null_counts = other164.null_counts;
  __isset = other164.__isset;
  return *this;
}
void ColumnIndex::printTo(std::ostream& out) const {
  using ::apache::thrift::to_string;
  out << "ColumnIndex(";
  out << "null_pages=" << to_string(null_pages);
  out << ", " << "min_values=" << to_string(min_values);
  out << ", " << "max_values=" << to_string(max_values);
  out << ", " << "boundary_order=" << to_string(boundary_order);
  out << ", " << "null_counts="; (__isset.null_counts ? (out << to_string(null_counts)) : (out << "<null>"));
  out << ")";
}


AesGcmV1::~AesGcmV1() throw() {
}


void AesGcmV1::__set_aad_prefix(const std::string& val) {
  this->aad_prefix = val;
__isset.aad_prefix = true;
}

void AesGcmV1::__set_aad_file_unique(const std::string& val) {
  this->aad_file_unique = val;
__isset.aad_file_unique = true;
}

void AesGcmV1::__set_supply_aad_prefix(const bool val) {
  this->supply_aad_prefix = val;
__isset.supply_aad_prefix = true;
}
std::ostream& operator<<(std::ostream& out, const AesGcmV1& obj)
{
  obj.printTo(out);
  return out;
}


uint32_t AesGcmV1::read(::apache::thrift::protocol::TProtocol* iprot) {

  ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
  uint32_t xfer = 0;
  std::string fname;
  ::apache::thrift::protocol::TType ftype;
  int16_t fid;

  xfer += iprot->readStructBegin(fname);

  using ::apache::thrift::protocol::TProtocolException;


  while (true)
  {
    xfer += iprot->readFieldBegin(fname, ftype, fid);
    if (ftype == ::apache::thrift::protocol::T_STOP) {
      break;
    }
    switch (fid)
    {
      case 1:
        if (ftype == ::apache::thrift::protocol::T_STRING) {
          xfer += iprot->readBinary(this->aad_prefix);
          this->__isset.aad_prefix = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 2:
        if (ftype == ::apache::thrift::protocol::T_STRING) {
          xfer += iprot->readBinary(this->aad_file_unique);
          this->__isset.aad_file_unique = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 3:
        if (ftype == ::apache::thrift::protocol::T_BOOL) {
          xfer += iprot->readBool(this->supply_aad_prefix);
          this->__isset.supply_aad_prefix = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      default:
        xfer += iprot->skip(ftype);
        break;
    }
    xfer += iprot->readFieldEnd();
  }

  xfer += iprot->readStructEnd();

  return xfer;
}

uint32_t AesGcmV1::write(::apache::thrift::protocol::TProtocol* oprot) const {
  uint32_t xfer = 0;
  ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
  xfer += oprot->writeStructBegin("AesGcmV1");

  if (this->__isset.aad_prefix) {
    xfer += oprot->writeFieldBegin("aad_prefix", ::apache::thrift::protocol::T_STRING, 1);
    xfer += oprot->writeBinary(this->aad_prefix);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.aad_file_unique) {
    xfer += oprot->writeFieldBegin("aad_file_unique", ::apache::thrift::protocol::T_STRING, 2);
    xfer += oprot->writeBinary(this->aad_file_unique);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.supply_aad_prefix) {
    xfer += oprot->writeFieldBegin("supply_aad_prefix", ::apache::thrift::protocol::T_BOOL, 3);
    xfer += oprot->writeBool(this->supply_aad_prefix);
    xfer += oprot->writeFieldEnd();
  }
  xfer += oprot->writeFieldStop();
  xfer += oprot->writeStructEnd();
  return xfer;
}

void swap(AesGcmV1 &a, AesGcmV1 &b) {
  using ::std::swap;
  swap(a.aad_prefix, b.aad_prefix);
  swap(a.aad_file_unique, b.aad_file_unique);
  swap(a.supply_aad_prefix, b.supply_aad_prefix);
  swap(a.__isset, b.__isset);
}

AesGcmV1::AesGcmV1(const AesGcmV1& other165) {
  aad_prefix = other165.aad_prefix;
  aad_file_unique = other165.aad_file_unique;
  supply_aad_prefix = other165.supply_aad_prefix;
  __isset = other165.__isset;
}
AesGcmV1& AesGcmV1::operator=(const AesGcmV1& other166) {
  aad_prefix = other166.aad_prefix;
  aad_file_unique = other166.aad_file_unique;
  supply_aad_prefix = other166.supply_aad_prefix;
  __isset = other166.__isset;
  return *this;
}
void AesGcmV1::printTo(std::ostream& out) const {
  using ::apache::thrift::to_string;
  out << "AesGcmV1(";
  out << "aad_prefix="; (__isset.aad_prefix ? (out << to_string(aad_prefix)) : (out << "<null>"));
  out << ", " << "aad_file_unique="; (__isset.aad_file_unique ? (out << to_string(aad_file_unique)) : (out << "<null>"));
  out << ", " << "supply_aad_prefix="; (__isset.supply_aad_prefix ? (out << to_string(supply_aad_prefix)) : (out << "<null>"));
  out << ")";
}


AesGcmCtrV1::~AesGcmCtrV1() throw() {
}


void AesGcmCtrV1::__set_aad_prefix(const std::string& val) {
  this->aad_prefix = val;
__isset.aad_prefix = true;
}

void AesGcmCtrV1::__set_aad_file_unique(const std::string& val) {
  this->aad_file_unique = val;
__isset.aad_file_unique = true;
}

void AesGcmCtrV1::__set_supply_aad_prefix(const bool val) {
  this->supply_aad_prefix = val;
__isset.supply_aad_prefix = true;
}
std::ostream& operator<<(std::ostream& out, const AesGcmCtrV1& obj)
{
  obj.printTo(out);
  return out;
}


uint32_t AesGcmCtrV1::read(::apache::thrift::protocol::TProtocol* iprot) {

  ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
  uint32_t xfer = 0;
  std::string fname;
  ::apache::thrift::protocol::TType ftype;
  int16_t fid;

  xfer += iprot->readStructBegin(fname);

  using ::apache::thrift::protocol::TProtocolException;


  while (true)
  {
    xfer += iprot->readFieldBegin(fname, ftype, fid);
    if (ftype == ::apache::thrift::protocol::T_STOP) {
      break;
    }
    switch (fid)
    {
      case 1:
        if (ftype == ::apache::thrift::protocol::T_STRING) {
          xfer += iprot->readBinary(this->aad_prefix);
          this->__isset.aad_prefix = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 2:
        if (ftype == ::apache::thrift::protocol::T_STRING) {
          xfer += iprot->readBinary(this->aad_file_unique);
          this->__isset.aad_file_unique = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 3:
        if (ftype == ::apache::thrift::protocol::T_BOOL) {
          xfer += iprot->readBool(this->supply_aad_prefix);
          this->__isset.supply_aad_prefix = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      default:
        xfer += iprot->skip(ftype);
        break;
    }
    xfer += iprot->readFieldEnd();
  }

  xfer += iprot->readStructEnd();

  return xfer;
}

uint32_t AesGcmCtrV1::write(::apache::thrift::protocol::TProtocol* oprot) const {
  uint32_t xfer = 0;
  ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
  xfer += oprot->writeStructBegin("AesGcmCtrV1");

  if (this->__isset.aad_prefix) {
    xfer += oprot->writeFieldBegin("aad_prefix", ::apache::thrift::protocol::T_STRING, 1);
    xfer += oprot->writeBinary(this->aad_prefix);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.aad_file_unique) {
    xfer += oprot->writeFieldBegin("aad_file_unique", ::apache::thrift::protocol::T_STRING, 2);
    xfer += oprot->writeBinary(this->aad_file_unique);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.supply_aad_prefix) {
    xfer += oprot->writeFieldBegin("supply_aad_prefix", ::apache::thrift::protocol::T_BOOL, 3);
    xfer += oprot->writeBool(this->supply_aad_prefix);
    xfer += oprot->writeFieldEnd();
  }
  xfer += oprot->writeFieldStop();
  xfer += oprot->writeStructEnd();
  return xfer;
}

void swap(AesGcmCtrV1 &a, AesGcmCtrV1 &b) {
  using ::std::swap;
  swap(a.aad_prefix, b.aad_prefix);
  swap(a.aad_file_unique, b.aad_file_unique);
  swap(a.supply_aad_prefix, b.supply_aad_prefix);
  swap(a.__isset, b.__isset);
}

AesGcmCtrV1::AesGcmCtrV1(const AesGcmCtrV1& other167) {
  aad_prefix = other167.aad_prefix;
  aad_file_unique = other167.aad_file_unique;
  supply_aad_prefix = other167.supply_aad_prefix;
  __isset = other167.__isset;
}
AesGcmCtrV1& AesGcmCtrV1::operator=(const AesGcmCtrV1& other168) {
  aad_prefix = other168.aad_prefix;
  aad_file_unique = other168.aad_file_unique;
  supply_aad_prefix = other168.supply_aad_prefix;
  __isset = other168.__isset;
  return *this;
}
void AesGcmCtrV1::printTo(std::ostream& out) const {
  using ::apache::thrift::to_string;
  out << "AesGcmCtrV1(";
  out << "aad_prefix="; (__isset.aad_prefix ? (out << to_string(aad_prefix)) : (out << "<null>"));
  out << ", " << "aad_file_unique="; (__isset.aad_file_unique ? (out << to_string(aad_file_unique)) : (out << "<null>"));
  out << ", " << "supply_aad_prefix="; (__isset.supply_aad_prefix ? (out << to_string(supply_aad_prefix)) : (out << "<null>"));
  out << ")";
}


EncryptionAlgorithm::~EncryptionAlgorithm() throw() {
}


void EncryptionAlgorithm::__set_AES_GCM_V1(const AesGcmV1& val) {
  this->AES_GCM_V1 = val;
__isset.AES_GCM_V1 = true;
}

void EncryptionAlgorithm::__set_AES_GCM_CTR_V1(const AesGcmCtrV1& val) {
  this->AES_GCM_CTR_V1 = val;
__isset.AES_GCM_CTR_V1 = true;
}
std::ostream& operator<<(std::ostream& out, const EncryptionAlgorithm& obj)
{
  obj.printTo(out);
  return out;
}


uint32_t EncryptionAlgorithm::read(::apache::thrift::protocol::TProtocol* iprot) {

  ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
  uint32_t xfer = 0;
  std::string fname;
  ::apache::thrift::protocol::TType ftype;
  int16_t fid;

  xfer += iprot->readStructBegin(fname);

  using ::apache::thrift::protocol::TProtocolException;


  while (true)
  {
    xfer += iprot->readFieldBegin(fname, ftype, fid);
    if (ftype == ::apache::thrift::protocol::T_STOP) {
      break;
    }
    switch (fid)
    {
      case 1:
        if (ftype == ::apache::thrift::protocol::T_STRUCT) {
          xfer += this->AES_GCM_V1.read(iprot);
          this->__isset.AES_GCM_V1 = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 2:
        if (ftype == ::apache::thrift::protocol::T_STRUCT) {
          xfer += this->AES_GCM_CTR_V1.read(iprot);
          this->__isset.AES_GCM_CTR_V1 = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      default:
        xfer += iprot->skip(ftype);
        break;
    }
    xfer += iprot->readFieldEnd();
  }

  xfer += iprot->readStructEnd();

  return xfer;
}

uint32_t EncryptionAlgorithm::write(::apache::thrift::protocol::TProtocol* oprot) const {
  uint32_t xfer = 0;
  ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
  xfer += oprot->writeStructBegin("EncryptionAlgorithm");

  if (this->__isset.AES_GCM_V1) {
    xfer += oprot->writeFieldBegin("AES_GCM_V1", ::apache::thrift::protocol::T_STRUCT, 1);
    xfer += this->AES_GCM_V1.write(oprot);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.AES_GCM_CTR_V1) {
    xfer += oprot->writeFieldBegin("AES_GCM_CTR_V1", ::apache::thrift::protocol::T_STRUCT, 2);
    xfer += this->AES_GCM_CTR_V1.write(oprot);
    xfer += oprot->writeFieldEnd();
  }
  xfer += oprot->writeFieldStop();
  xfer += oprot->writeStructEnd();
  return xfer;
}

void swap(EncryptionAlgorithm &a, EncryptionAlgorithm &b) {
  using ::std::swap;
  swap(a.AES_GCM_V1, b.AES_GCM_V1);
  swap(a.AES_GCM_CTR_V1, b.AES_GCM_CTR_V1);
  swap(a.__isset, b.__isset);
}

EncryptionAlgorithm::EncryptionAlgorithm(const EncryptionAlgorithm& other169) {
  AES_GCM_V1 = other169.AES_GCM_V1;
  AES_GCM_CTR_V1 = other169.AES_GCM_CTR_V1;
  __isset = other169.__isset;
}
EncryptionAlgorithm& EncryptionAlgorithm::operator=(const EncryptionAlgorithm& other170) {
  AES_GCM_V1 = other170.AES_GCM_V1;
  AES_GCM_CTR_V1 = other170.AES_GCM_CTR_V1;
  __isset = other170.__isset;
  return *this;
}
void EncryptionAlgorithm::printTo(std::ostream& out) const {
  using ::apache::thrift::to_string;
  out << "EncryptionAlgorithm(";
  out << "AES_GCM_V1="; (__isset.AES_GCM_V1 ? (out << to_string(AES_GCM_V1)) : (out << "<null>"));
  out << ", " << "AES_GCM_CTR_V1="; (__isset.AES_GCM_CTR_V1 ? (out << to_string(AES_GCM_CTR_V1)) : (out << "<null>"));
  out << ")";
}


FileMetaData::~FileMetaData() throw() {
}


void FileMetaData::__set_version(const int32_t val) {
  this->version = val;
}

void FileMetaData::__set_schema(const std::vector<SchemaElement> & val) {
  this->schema = val;
}

void FileMetaData::__set_num_rows(const int64_t val) {
  this->num_rows = val;
}

void FileMetaData::__set_row_groups(const std::vector<RowGroup> & val) {
  this->row_groups = val;
}

void FileMetaData::__set_key_value_metadata(const std::vector<KeyValue> & val) {
  this->key_value_metadata = val;
__isset.key_value_metadata = true;
}

void FileMetaData::__set_created_by(const std::string& val) {
  this->created_by = val;
__isset.created_by = true;
}

void FileMetaData::__set_column_orders(const std::vector<ColumnOrder> & val) {
  this->column_orders = val;
__isset.column_orders = true;
}

void FileMetaData::__set_encryption_algorithm(const EncryptionAlgorithm& val) {
  this->encryption_algorithm = val;
__isset.encryption_algorithm = true;
}

void FileMetaData::__set_footer_signing_key_metadata(const std::string& val) {
  this->footer_signing_key_metadata = val;
__isset.footer_signing_key_metadata = true;
}
std::ostream& operator<<(std::ostream& out, const FileMetaData& obj)
{
  obj.printTo(out);
  return out;
}


uint32_t FileMetaData::read(::apache::thrift::protocol::TProtocol* iprot) {

  ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
  uint32_t xfer = 0;
  std::string fname;
  ::apache::thrift::protocol::TType ftype;
  int16_t fid;

  xfer += iprot->readStructBegin(fname);

  using ::apache::thrift::protocol::TProtocolException;

  bool isset_version = false;
  bool isset_schema = false;
  bool isset_num_rows = false;
  bool isset_row_groups = false;

  while (true)
  {
    xfer += iprot->readFieldBegin(fname, ftype, fid);
    if (ftype == ::apache::thrift::protocol::T_STOP) {
      break;
    }
    switch (fid)
    {
      case 1:
        if (ftype == ::apache::thrift::protocol::T_I32) {
          xfer += iprot->readI32(this->version);
          isset_version = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 2:
        if (ftype == ::apache::thrift::protocol::T_LIST) {
          {
            this->schema.clear();
            uint32_t _size171;
            ::apache::thrift::protocol::TType _etype174;
            xfer += iprot->readListBegin(_etype174, _size171);
            this->schema.resize(_size171);
            uint32_t _i175;
            for (_i175 = 0; _i175 < _size171; ++_i175)
            {
              xfer += this->schema[_i175].read(iprot);
            }
            xfer += iprot->readListEnd();
          }
          isset_schema = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 3:
        if (ftype == ::apache::thrift::protocol::T_I64) {
          xfer += iprot->readI64(this->num_rows);
          isset_num_rows = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 4:
        if (ftype == ::apache::thrift::protocol::T_LIST) {
          {
            this->row_groups.clear();
            uint32_t _size176;
            ::apache::thrift::protocol::TType _etype179;
            xfer += iprot->readListBegin(_etype179, _size176);
            this->row_groups.resize(_size176);
            uint32_t _i180;
            for (_i180 = 0; _i180 < _size176; ++_i180)
            {
              xfer += this->row_groups[_i180].read(iprot);
            }
            xfer += iprot->readListEnd();
          }
          isset_row_groups = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 5:
        if (ftype == ::apache::thrift::protocol::T_LIST) {
          {
            this->key_value_metadata.clear();
            uint32_t _size181;
            ::apache::thrift::protocol::TType _etype184;
            xfer += iprot->readListBegin(_etype184, _size181);
            this->key_value_metadata.resize(_size181);
            uint32_t _i185;
            for (_i185 = 0; _i185 < _size181; ++_i185)
            {
              xfer += this->key_value_metadata[_i185].read(iprot);
            }
            xfer += iprot->readListEnd();
          }
          this->__isset.key_value_metadata = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 6:
        if (ftype == ::apache::thrift::protocol::T_STRING) {
          xfer += iprot->readString(this->created_by);
          this->__isset.created_by = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 7:
        if (ftype == ::apache::thrift::protocol::T_LIST) {
          {
            this->column_orders.clear();
            uint32_t _size186;
            ::apache::thrift::protocol::TType _etype189;
            xfer += iprot->readListBegin(_etype189, _size186);
            this->column_orders.resize(_size186);
            uint32_t _i190;
            for (_i190 = 0; _i190 < _size186; ++_i190)
            {
              xfer += this->column_orders[_i190].read(iprot);
            }
            xfer += iprot->readListEnd();
          }
          this->__isset.column_orders = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 8:
        if (ftype == ::apache::thrift::protocol::T_STRUCT) {
          xfer += this->encryption_algorithm.read(iprot);
          this->__isset.encryption_algorithm = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 9:
        if (ftype == ::apache::thrift::protocol::T_STRING) {
          xfer += iprot->readBinary(this->footer_signing_key_metadata);
          this->__isset.footer_signing_key_metadata = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      default:
        xfer += iprot->skip(ftype);
        break;
    }
    xfer += iprot->readFieldEnd();
  }

  xfer += iprot->readStructEnd();

  if (!isset_version)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  if (!isset_schema)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  if (!isset_num_rows)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  if (!isset_row_groups)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  return xfer;
}

uint32_t FileMetaData::write(::apache::thrift::protocol::TProtocol* oprot) const {
  uint32_t xfer = 0;
  ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
  xfer += oprot->writeStructBegin("FileMetaData");

  xfer += oprot->writeFieldBegin("version", ::apache::thrift::protocol::T_I32, 1);
  xfer += oprot->writeI32(this->version);
  xfer += oprot->writeFieldEnd();

  xfer += oprot->writeFieldBegin("schema", ::apache::thrift::protocol::T_LIST, 2);
  {
    xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, static_cast<uint32_t>(this->schema.size()));
    std::vector<SchemaElement> ::const_iterator _iter191;
    for (_iter191 = this->schema.begin(); _iter191 != this->schema.end(); ++_iter191)
    {
      xfer += (*_iter191).write(oprot);
    }
    xfer += oprot->writeListEnd();
  }
  xfer += oprot->writeFieldEnd();

  xfer += oprot->writeFieldBegin("num_rows", ::apache::thrift::protocol::T_I64, 3);
  xfer += oprot->writeI64(this->num_rows);
  xfer += oprot->writeFieldEnd();

  xfer += oprot->writeFieldBegin("row_groups", ::apache::thrift::protocol::T_LIST, 4);
  {
    xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, static_cast<uint32_t>(this->row_groups.size()));
    std::vector<RowGroup> ::const_iterator _iter192;
    for (_iter192 = this->row_groups.begin(); _iter192 != this->row_groups.end(); ++_iter192)
    {
      xfer += (*_iter192).write(oprot);
    }
    xfer += oprot->writeListEnd();
  }
  xfer += oprot->writeFieldEnd();

  if (this->__isset.key_value_metadata) {
    xfer += oprot->writeFieldBegin("key_value_metadata", ::apache::thrift::protocol::T_LIST, 5);
    {
      xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, static_cast<uint32_t>(this->key_value_metadata.size()));
      std::vector<KeyValue> ::const_iterator _iter193;
      for (_iter193 = this->key_value_metadata.begin(); _iter193 != this->key_value_metadata.end(); ++_iter193)
      {
        xfer += (*_iter193).write(oprot);
      }
      xfer += oprot->writeListEnd();
    }
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.created_by) {
    xfer += oprot->writeFieldBegin("created_by", ::apache::thrift::protocol::T_STRING, 6);
    xfer += oprot->writeString(this->created_by);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.column_orders) {
    xfer += oprot->writeFieldBegin("column_orders", ::apache::thrift::protocol::T_LIST, 7);
    {
      xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, static_cast<uint32_t>(this->column_orders.size()));
      std::vector<ColumnOrder> ::const_iterator _iter194;
      for (_iter194 = this->column_orders.begin(); _iter194 != this->column_orders.end(); ++_iter194)
      {
        xfer += (*_iter194).write(oprot);
      }
      xfer += oprot->writeListEnd();
    }
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.encryption_algorithm) {
    xfer += oprot->writeFieldBegin("encryption_algorithm", ::apache::thrift::protocol::T_STRUCT, 8);
    xfer += this->encryption_algorithm.write(oprot);
    xfer += oprot->writeFieldEnd();
  }
  if (this->__isset.footer_signing_key_metadata) {
    xfer += oprot->writeFieldBegin("footer_signing_key_metadata", ::apache::thrift::protocol::T_STRING, 9);
    xfer += oprot->writeBinary(this->footer_signing_key_metadata);
    xfer += oprot->writeFieldEnd();
  }
  xfer += oprot->writeFieldStop();
  xfer += oprot->writeStructEnd();
  return xfer;
}

void swap(FileMetaData &a, FileMetaData &b) {
  using ::std::swap;
  swap(a.version, b.version);
  swap(a.schema, b.schema);
  swap(a.num_rows, b.num_rows);
  swap(a.row_groups, b.row_groups);
  swap(a.key_value_metadata, b.key_value_metadata);
  swap(a.created_by, b.created_by);
  swap(a.column_orders, b.column_orders);
  swap(a.encryption_algorithm, b.encryption_algorithm);
  swap(a.footer_signing_key_metadata, b.footer_signing_key_metadata);
  swap(a.__isset, b.__isset);
}

FileMetaData::FileMetaData(const FileMetaData& other195) {
  version = other195.version;
  schema = other195.schema;
  num_rows = other195.num_rows;
  row_groups = other195.row_groups;
  key_value_metadata = other195.key_value_metadata;
  created_by = other195.created_by;
  column_orders = other195.column_orders;
  encryption_algorithm = other195.encryption_algorithm;
  footer_signing_key_metadata = other195.footer_signing_key_metadata;
  __isset = other195.__isset;
}
FileMetaData& FileMetaData::operator=(const FileMetaData& other196) {
  version = other196.version;
  schema = other196.schema;
  num_rows = other196.num_rows;
  row_groups = other196.row_groups;
  key_value_metadata = other196.key_value_metadata;
  created_by = other196.created_by;
  column_orders = other196.column_orders;
  encryption_algorithm = other196.encryption_algorithm;
  footer_signing_key_metadata = other196.footer_signing_key_metadata;
  __isset = other196.__isset;
  return *this;
}
void FileMetaData::printTo(std::ostream& out) const {
  using ::apache::thrift::to_string;
  out << "FileMetaData(";
  out << "version=" << to_string(version);
  out << ", " << "schema=" << to_string(schema);
  out << ", " << "num_rows=" << to_string(num_rows);
  out << ", " << "row_groups=" << to_string(row_groups);
  out << ", " << "key_value_metadata="; (__isset.key_value_metadata ? (out << to_string(key_value_metadata)) : (out << "<null>"));
  out << ", " << "created_by="; (__isset.created_by ? (out << to_string(created_by)) : (out << "<null>"));
  out << ", " << "column_orders="; (__isset.column_orders ? (out << to_string(column_orders)) : (out << "<null>"));
  out << ", " << "encryption_algorithm="; (__isset.encryption_algorithm ? (out << to_string(encryption_algorithm)) : (out << "<null>"));
  out << ", " << "footer_signing_key_metadata="; (__isset.footer_signing_key_metadata ? (out << to_string(footer_signing_key_metadata)) : (out << "<null>"));
  out << ")";
}


FileCryptoMetaData::~FileCryptoMetaData() throw() {
}


void FileCryptoMetaData::__set_encryption_algorithm(const EncryptionAlgorithm& val) {
  this->encryption_algorithm = val;
}

void FileCryptoMetaData::__set_key_metadata(const std::string& val) {
  this->key_metadata = val;
__isset.key_metadata = true;
}
std::ostream& operator<<(std::ostream& out, const FileCryptoMetaData& obj)
{
  obj.printTo(out);
  return out;
}


uint32_t FileCryptoMetaData::read(::apache::thrift::protocol::TProtocol* iprot) {

  ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
  uint32_t xfer = 0;
  std::string fname;
  ::apache::thrift::protocol::TType ftype;
  int16_t fid;

  xfer += iprot->readStructBegin(fname);

  using ::apache::thrift::protocol::TProtocolException;

  bool isset_encryption_algorithm = false;

  while (true)
  {
    xfer += iprot->readFieldBegin(fname, ftype, fid);
    if (ftype == ::apache::thrift::protocol::T_STOP) {
      break;
    }
    switch (fid)
    {
      case 1:
        if (ftype == ::apache::thrift::protocol::T_STRUCT) {
          xfer += this->encryption_algorithm.read(iprot);
          isset_encryption_algorithm = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      case 2:
        if (ftype == ::apache::thrift::protocol::T_STRING) {
          xfer += iprot->readBinary(this->key_metadata);
          this->__isset.key_metadata = true;
        } else {
          xfer += iprot->skip(ftype);
        }
        break;
      default:
        xfer += iprot->skip(ftype);
        break;
    }
    xfer += iprot->readFieldEnd();
  }

  xfer += iprot->readStructEnd();

  if (!isset_encryption_algorithm)
    throw TProtocolException(TProtocolException::INVALID_DATA);
  return xfer;
}

uint32_t FileCryptoMetaData::write(::apache::thrift::protocol::TProtocol* oprot) const {
  uint32_t xfer = 0;
  ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
  xfer += oprot->writeStructBegin("FileCryptoMetaData");

  xfer += oprot->writeFieldBegin("encryption_algorithm", ::apache::thrift::protocol::T_STRUCT, 1);
  xfer += this->encryption_algorithm.write(oprot);
  xfer += oprot->writeFieldEnd();

  if (this->__isset.key_metadata) {
    xfer += oprot->writeFieldBegin("key_metadata", ::apache::thrift::protocol::T_STRING, 2);
    xfer += oprot->writeBinary(this->key_metadata);
    xfer += oprot->writeFieldEnd();
  }
  xfer += oprot->writeFieldStop();
  xfer += oprot->writeStructEnd();
  return xfer;
}

void swap(FileCryptoMetaData &a, FileCryptoMetaData &b) {
  using ::std::swap;
  swap(a.encryption_algorithm, b.encryption_algorithm);
  swap(a.key_metadata, b.key_metadata);
  swap(a.__isset, b.__isset);
}

FileCryptoMetaData::FileCryptoMetaData(const FileCryptoMetaData& other197) {
  encryption_algorithm = other197.encryption_algorithm;
  key_metadata = other197.key_metadata;
  __isset = other197.__isset;
}
FileCryptoMetaData& FileCryptoMetaData::operator=(const FileCryptoMetaData& other198) {
  encryption_algorithm = other198.encryption_algorithm;
  key_metadata = other198.key_metadata;
  __isset = other198.__isset;
  return *this;
}
void FileCryptoMetaData::printTo(std::ostream& out) const {
  using ::apache::thrift::to_string;
  out << "FileCryptoMetaData(";
  out << "encryption_algorithm=" << to_string(encryption_algorithm);
  out << ", " << "key_metadata="; (__isset.key_metadata ? (out << to_string(key_metadata)) : (out << "<null>"));
  out << ")";
}

}} // namespace

/**
 * Autogenerated by Thrift Compiler (0.11.0)
 *
 * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
 *  @generated
 */
// #include "parquet_constants.h"

namespace parquet { namespace format {

const parquetConstants g_parquet_constants;

parquetConstants::parquetConstants() {
}

}} // namespace


// Copyright 2011 Google Inc. All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Various type stubs for the open-source version of Snappy.
//
// // This file cannot include config.h, as it is included from snappy.h,
// // which is a public header. Instead, snappy-stubs-public.h is generated by
// // from snappy-stubs-public.h.in at configure time.

#ifndef THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_
#define THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_

#include <cstddef>
#include <cstdint>
#include <string>

#ifndef _WIN32  // HAVE_SYS_UIO_H
#include <sys/uio.h>
#endif  // HAVE_SYS_UIO_H

#define SNAPPY_MAJOR 1
#define SNAPPY_MINOR 1
#define SNAPPY_PATCHLEVEL 7
#define SNAPPY_VERSION \
    ((SNAPPY_MAJOR << 16) | (SNAPPY_MINOR << 8) | SNAPPY_PATCHLEVEL)

namespace snappy {

using int8 = std::int8_t;
using uint8 = std::uint8_t;
using int16 = std::int16_t;
using uint16 = std::uint16_t;
using int32 = std::int32_t;
using uint32 = std::uint32_t;
using int64 = std::int64_t;
using uint64 = std::uint64_t;

using string = std::string;

#ifdef _WIN32  // !HAVE_SYS_UIO_H
// Windows does not have an iovec type, yet the concept is universally useful.
// It is simple to define it ourselves, so we put it inside our own namespace.
struct iovec {
  void* iov_base;
  size_t iov_len;
};
#endif  // !HAVE_SYS_UIO_H

}  // namespace snappy

#endif  // THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_

// Copyright 2005 and onwards Google Inc.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// A light-weight compression algorithm.  It is designed for speed of
// compression and decompression, rather than for the utmost in space
// savings.
//
// For getting better compression ratios when you are compressing data
// with long repeated sequences or compressing data that is similar to
// other data, while still compressing fast, you might look at first
// using BMDiff and then compressing the output of BMDiff with
// Snappy.

#ifndef THIRD_PARTY_SNAPPY_SNAPPY_H__
#define THIRD_PARTY_SNAPPY_SNAPPY_H__

#include <cstddef>
#include <string>

// #include "snappy-stubs-public.h"

namespace snappy {
  class Source;
  class Sink;

  // ------------------------------------------------------------------------
  // Generic compression/decompression routines.
  // ------------------------------------------------------------------------

  // Compress the bytes read from "*source" and append to "*sink". Return the
  // number of bytes written.
  size_t Compress(Source* source, Sink* sink);

  // Find the uncompressed length of the given stream, as given by the header.
  // Note that the true length could deviate from this; the stream could e.g.
  // be truncated.
  //
  // Also note that this leaves "*source" in a state that is unsuitable for
  // further operations, such as RawUncompress(). You will need to rewind
  // or recreate the source yourself before attempting any further calls.
  bool GetUncompressedLength(Source* source, uint32* result);

  // ------------------------------------------------------------------------
  // Higher-level string based routines (should be sufficient for most users)
  // ------------------------------------------------------------------------

  // Sets "*output" to the compressed version of "input[0,input_length-1]".
  // Original contents of *output are lost.
  //
  // REQUIRES: "input[]" is not an alias of "*output".
  size_t Compress(const char* input, size_t input_length, string* output);

  // Decompresses "compressed[0,compressed_length-1]" to "*uncompressed".
  // Original contents of "*uncompressed" are lost.
  //
  // REQUIRES: "compressed[]" is not an alias of "*uncompressed".
  //
  // returns false if the message is corrupted and could not be decompressed
  bool Uncompress(const char* compressed, size_t compressed_length,
                  string* uncompressed);

  // Decompresses "compressed" to "*uncompressed".
  //
  // returns false if the message is corrupted and could not be decompressed
  bool Uncompress(Source* compressed, Sink* uncompressed);

  // This routine uncompresses as much of the "compressed" as possible
  // into sink.  It returns the number of valid bytes added to sink
  // (extra invalid bytes may have been added due to errors; the caller
  // should ignore those). The emitted data typically has length
  // GetUncompressedLength(), but may be shorter if an error is
  // encountered.
  size_t UncompressAsMuchAsPossible(Source* compressed, Sink* uncompressed);

  // ------------------------------------------------------------------------
  // Lower-level character array based routines.  May be useful for
  // efficiency reasons in certain circumstances.
  // ------------------------------------------------------------------------

  // REQUIRES: "compressed" must point to an area of memory that is at
  // least "MaxCompressedLength(input_length)" bytes in length.
  //
  // Takes the data stored in "input[0..input_length]" and stores
  // it in the array pointed to by "compressed".
  //
  // "*compressed_length" is set to the length of the compressed output.
  //
  // Example:
  //    char* output = new char[snappy::MaxCompressedLength(input_length)];
  //    size_t output_length;
  //    RawCompress(input, input_length, output, &output_length);
  //    ... Process(output, output_length) ...
  //    delete [] output;
  void RawCompress(const char* input,
                   size_t input_length,
                   char* compressed,
                   size_t* compressed_length);

  // Given data in "compressed[0..compressed_length-1]" generated by
  // calling the Snappy::Compress routine, this routine
  // stores the uncompressed data to
  //    uncompressed[0..GetUncompressedLength(compressed)-1]
  // returns false if the message is corrupted and could not be decrypted
  bool RawUncompress(const char* compressed, size_t compressed_length,
                     char* uncompressed);

  // Given data from the byte source 'compressed' generated by calling
  // the Snappy::Compress routine, this routine stores the uncompressed
  // data to
  //    uncompressed[0..GetUncompressedLength(compressed,compressed_length)-1]
  // returns false if the message is corrupted and could not be decrypted
  bool RawUncompress(Source* compressed, char* uncompressed);

  // Given data in "compressed[0..compressed_length-1]" generated by
  // calling the Snappy::Compress routine, this routine
  // stores the uncompressed data to the iovec "iov". The number of physical
  // buffers in "iov" is given by iov_cnt and their cumulative size
  // must be at least GetUncompressedLength(compressed). The individual buffers
  // in "iov" must not overlap with each other.
  //
  // returns false if the message is corrupted and could not be decrypted
  bool RawUncompressToIOVec(const char* compressed, size_t compressed_length,
                            const struct iovec* iov, size_t iov_cnt);

  // Given data from the byte source 'compressed' generated by calling
  // the Snappy::Compress routine, this routine stores the uncompressed
  // data to the iovec "iov". The number of physical
  // buffers in "iov" is given by iov_cnt and their cumulative size
  // must be at least GetUncompressedLength(compressed). The individual buffers
  // in "iov" must not overlap with each other.
  //
  // returns false if the message is corrupted and could not be decrypted
  bool RawUncompressToIOVec(Source* compressed, const struct iovec* iov,
                            size_t iov_cnt);

  // Returns the maximal size of the compressed representation of
  // input data that is "source_bytes" bytes in length;
  size_t MaxCompressedLength(size_t source_bytes);

  // REQUIRES: "compressed[]" was produced by RawCompress() or Compress()
  // Returns true and stores the length of the uncompressed data in
  // *result normally.  Returns false on parsing error.
  // This operation takes O(1) time.
  bool GetUncompressedLength(const char* compressed, size_t compressed_length,
                             size_t* result);

  // Returns true iff the contents of "compressed[]" can be uncompressed
  // successfully.  Does not return the uncompressed data.  Takes
  // time proportional to compressed_length, but is usually at least
  // a factor of four faster than actual decompression.
  bool IsValidCompressedBuffer(const char* compressed,
                               size_t compressed_length);

  // Returns true iff the contents of "compressed" can be uncompressed
  // successfully.  Does not return the uncompressed data.  Takes
  // time proportional to *compressed length, but is usually at least
  // a factor of four faster than actual decompression.
  // On success, consumes all of *compressed.  On failure, consumes an
  // unspecified prefix of *compressed.
  bool IsValidCompressed(Source* compressed);

}  // end namespace snappy

#endif  // THIRD_PARTY_SNAPPY_SNAPPY_H__


#include <string>
#include <cassert>
#include <cstring>

namespace duckdb {

enum class UnicodeType {INVALID, ASCII, UNICODE};


class Utf8Proc {
public:
	static UnicodeType Analyze(const char* s) {
		return Analyze(s, std::strlen(s));
	}

	static UnicodeType Analyze(std::string s) {
		return Analyze(s.c_str(), s.size());
	}

	static UnicodeType Analyze(const char *s, size_t len);


	static std::string Normalize(std::string s);

	static char* Normalize(const char* s);

	//! Returns whether or not the UTF8 string is valid
	static bool IsValid(const char *s, size_t len);
	//! Returns the position (in bytes) of the next grapheme cluster
	static size_t NextGraphemeCluster(const char *s, size_t len, size_t pos);
	//! Returns the position (in bytes) of the previous grapheme cluster
	static size_t PreviousGraphemeCluster(const char *s, size_t len, size_t pos);
};

}

#include <string>
#include <vector>
#include <bitset>
#include <fstream>
#include <cstring>
#include <iostream>

// #include "parquet-extension.hpp"

#ifndef DUCKDB_AMALGAMATION
#include "duckdb/function/table_function.hpp"
#include "duckdb/parser/parsed_data/create_table_function_info.hpp"
#include "duckdb/main/client_context.hpp"
#include "duckdb/main/connection.hpp"
#include "duckdb/common/types/date.hpp"
#include "duckdb/common/types/timestamp.hpp"
#include "utf8proc_wrapper.hpp"

// #include "thrift/protocol/TCompactProtocol.h"
// #include "thrift/transport/TBufferTransports.h"
// #include "parquet_types.h"
// #include "snappy.h"
#endif

using namespace duckdb;
using namespace std;

using namespace parquet;
using namespace parquet::format;
using namespace apache::thrift;
using namespace apache::thrift::protocol;
using namespace apache::thrift::transport;

struct Int96 {
	uint32_t value[3];
};

class ByteBuffer { // on to the 10 thousandth impl
public:
	char *ptr = nullptr;
	uint64_t len = 0;

	ByteBuffer(){};
	ByteBuffer(char *ptr, uint64_t len) : ptr(ptr), len(len){};

	void inc(uint64_t increment) {
		available(increment);
		len -= increment;
		ptr += increment;
	}

	template <class T> T read() {
		available(sizeof(T));
		T val = *(T *)ptr;
		inc(sizeof(T));
		return val;
	}

	void copy_to(char *dest, uint64_t len) {
		available(len);
		memcpy(dest, ptr, len);
	}

	void available(uint64_t req_len) {
		if (req_len > len) {
			throw runtime_error("Out of buffer");
		}
	}
};

class ResizeableBuffer : public ByteBuffer {
public:
	void resize(uint64_t new_size) {
		if (new_size > len) {
			auto new_holder = std::unique_ptr<char[]>(new char[new_size]);
			holder = move(new_holder);
		}
		len = new_size;
		ptr = holder.get();
	}

private:
	std::unique_ptr<char[]> holder = nullptr;
};

static TCompactProtocolFactoryT<TMemoryBuffer> tproto_factory;

template <class T> static void thrift_unpack(const uint8_t *buf, uint32_t *len, T *deserialized_msg) {
	shared_ptr<TMemoryBuffer> tmem_transport(new TMemoryBuffer(const_cast<uint8_t *>(buf), *len));
	shared_ptr<TProtocol> tproto = tproto_factory.getProtocol(tmem_transport);
	try {
		deserialized_msg->read(tproto.get());
	} catch (std::exception &e) {
		std::stringstream ss;
		ss << "Couldn't deserialize thrift: " << e.what() << "\n";
		throw std::runtime_error(ss.str());
	}
	uint32_t bytes_left = tmem_transport->available_read();
	*len = *len - bytes_left;
}

// adapted from arrow parquet reader
class RleBpDecoder {

public:
	/// Create a decoder object. buffer/buffer_len is the decoded data.
	/// bit_width is the width of each value (before encoding).
	RleBpDecoder(const uint8_t *buffer, uint32_t buffer_len, uint32_t bit_width)
	    : buffer(buffer), bit_width_(bit_width), current_value_(0), repeat_count_(0), literal_count_(0) {

		if (bit_width >= 64) {
			throw runtime_error("Decode bit width too large");
		}
		byte_encoded_len = ((bit_width_ + 7) / 8);
		max_val = (1 << bit_width_) - 1;
	}

	/// Gets a batch of values.  Returns the number of decoded elements.
	template <typename T> void GetBatch(char *values_target_ptr, uint32_t batch_size) {
		auto values = (T *)values_target_ptr;
		uint32_t values_read = 0;

		while (values_read < batch_size) {
			if (repeat_count_ > 0) {
				int repeat_batch = std::min(batch_size - values_read, static_cast<uint32_t>(repeat_count_));
				std::fill(values + values_read, values + values_read + repeat_batch, static_cast<T>(current_value_));
				repeat_count_ -= repeat_batch;
				values_read += repeat_batch;
			} else if (literal_count_ > 0) {
				uint32_t literal_batch = std::min(batch_size - values_read, static_cast<uint32_t>(literal_count_));
				uint32_t actual_read = BitUnpack<T>(values + values_read, literal_batch);
				if (literal_batch != actual_read) {
					throw runtime_error("Did not find enough values");
				}
				literal_count_ -= literal_batch;
				values_read += literal_batch;
			} else {
				if (!NextCounts<T>()) {
					if (values_read != batch_size) {
						throw runtime_error("RLE decode did not find enough values");
					}
					return;
				}
			}
		}
		if (values_read != batch_size) {
			throw runtime_error("RLE decode did not find enough values");
		}
	}

private:
	const uint8_t *buffer;

	/// Number of bits needed to encode the value. Must be between 0 and 64.
	int bit_width_;
	uint64_t current_value_;
	uint32_t repeat_count_;
	uint32_t literal_count_;
	uint8_t byte_encoded_len;
	uint32_t max_val;

	int8_t bitpack_pos = 0;

	// this is slow but whatever, calls are rare
	static uint8_t VarintDecode(const uint8_t *source, uint32_t *result_out) {
		uint32_t result = 0;
		uint8_t shift = 0;
		uint8_t len = 0;
		while (true) {
			auto byte = *source++;
			len++;
			result |= (byte & 127) << shift;
			if ((byte & 128) == 0)
				break;
			shift += 7;
			if (shift > 32) {
				throw runtime_error("Varint-decoding found too large number");
			}
		}
		*result_out = result;
		return len;
	}

	/// Fills literal_count_ and repeat_count_ with next values. Returns false if there
	/// are no more.
	template <typename T> bool NextCounts() {
		// Read the next run's indicator int, it could be a literal or repeated run.
		// The int is encoded as a vlq-encoded value.
		uint32_t indicator_value;
		if (bitpack_pos != 0) {
			buffer++;
			bitpack_pos = 0;
		}
		buffer += VarintDecode(buffer, &indicator_value);

		// lsb indicates if it is a literal run or repeated run
		bool is_literal = indicator_value & 1;
		if (is_literal) {
			literal_count_ = (indicator_value >> 1) * 8;
		} else {
			repeat_count_ = indicator_value >> 1;
			// (ARROW-4018) this is not big-endian compatible, lol
			current_value_ = 0;
			for (auto i = 0; i < byte_encoded_len; i++) {
				current_value_ |= ((uint8_t)*buffer++) << (i * 8);
			}
			// sanity check
			if (repeat_count_ > 0 && current_value_ > max_val) {
				throw runtime_error("Payload value bigger than allowed. Corrupted file?");
			}
		}
		// TODO complain if we run out of buffer
		return true;
	}

	// somewhat optimized implementation that avoids non-alignment

	static const uint32_t BITPACK_MASKS[];
	static const uint8_t BITPACK_DLEN;

	template <typename T> uint32_t BitUnpack(T *dest, uint32_t count) {
		assert(bit_width_ < 32);

		// auto source = buffer;
		auto mask = BITPACK_MASKS[bit_width_];

		for (uint32_t i = 0; i < count; i++) {
			T val = (*buffer >> bitpack_pos) & mask;
			bitpack_pos += bit_width_;
			while (bitpack_pos > BITPACK_DLEN) {
				val |= (*++buffer << (BITPACK_DLEN - (bitpack_pos - bit_width_))) & mask;
				bitpack_pos -= BITPACK_DLEN;
			}
			dest[i] = val;
		}
		return count;
	}
};

const uint32_t RleBpDecoder::BITPACK_MASKS[] = {
    0,       1,       3,        7,        15,       31,        63,        127,       255,        511,       1023,
    2047,    4095,    8191,     16383,    32767,    65535,     131071,    262143,    524287,     1048575,   2097151,
    4194303, 8388607, 16777215, 33554431, 67108863, 134217727, 268435455, 536870911, 1073741823, 2147483647};

const uint8_t RleBpDecoder::BITPACK_DLEN = 8;

// surely they are joking
static constexpr int64_t kJulianToUnixEpochDays = 2440588LL;
static constexpr int64_t kMillisecondsInADay = 86400000LL;
static constexpr int64_t kNanosecondsInADay = kMillisecondsInADay * 1000LL * 1000LL;

static int64_t impala_timestamp_to_nanoseconds(const Int96 &impala_timestamp) {
	int64_t days_since_epoch = impala_timestamp.value[2] - kJulianToUnixEpochDays;
	int64_t nanoseconds = *(reinterpret_cast<const int64_t *>(&(impala_timestamp.value)));
	return days_since_epoch * kNanosecondsInADay + nanoseconds;
}

struct ParquetScanColumnData {
	idx_t chunk_offset;

	idx_t page_offset;
	idx_t page_value_count = 0;

	idx_t dict_size;

	ResizeableBuffer buf;
	ResizeableBuffer decompressed_buf; // only used for compressed files
	ResizeableBuffer dict;
	ResizeableBuffer offset_buf;
	ResizeableBuffer defined_buf;

	ByteBuffer payload;

	Encoding::type page_encoding;
	// these point into buf or decompressed_buf
	unique_ptr<RleBpDecoder> defined_decoder;
	unique_ptr<RleBpDecoder> dict_decoder;

	unique_ptr<ChunkCollection> string_collection;
};

struct ParquetScanFunctionData : public TableFunctionData {
	int64_t current_group;
	int64_t group_offset;

	ifstream pfile;

	FileMetaData file_meta_data;
	vector<SQLType> sql_types;
	vector<ParquetScanColumnData> column_data;
	bool finished;
};

class ParquetScanFunction : public TableFunction {
public:
	ParquetScanFunction()
	    : TableFunction("parquet_scan", {SQLType::VARCHAR}, parquet_scan_bind, parquet_scan_function, nullptr){};

private:
	static unique_ptr<FunctionData> parquet_scan_bind(ClientContext &context, vector<Value> inputs,
	                                                  vector<SQLType> &return_types, vector<string> &names) {

		auto file_name = inputs[0].GetValue<string>();
		auto res = make_unique<ParquetScanFunctionData>();

		auto &pfile = res->pfile;
		auto &file_meta_data = res->file_meta_data;

		pfile.open(file_name, std::ios::binary);

		ResizeableBuffer buf;
		buf.resize(4);
		memset(buf.ptr, '\0', 4);
		// check for magic bytes at start of file
		pfile.read(buf.ptr, 4);
		if (strncmp(buf.ptr, "PAR1", 4) != 0) {
			throw runtime_error("File not found or missing magic bytes");
		}

		// check for magic bytes at end of file
		pfile.seekg(-4, ios_base::end);
		pfile.read(buf.ptr, 4);
		if (strncmp(buf.ptr, "PAR1", 4) != 0) {
			throw runtime_error("No magic bytes found at end of file");
		}

		// read four-byte footer length from just before the end magic bytes
		pfile.seekg(-8, ios_base::end);
		pfile.read(buf.ptr, 4);
		int32_t footer_len = *(uint32_t *)buf.ptr;
		if (footer_len == 0) {
			throw runtime_error("Footer length can't be 0");
		}

		// read footer into buffer and de-thrift
		buf.resize(footer_len);
		pfile.seekg(-(footer_len + 8), ios_base::end);
		pfile.read(buf.ptr, footer_len);
		if (!pfile) {
			throw runtime_error("Could not read footer");
		}

		thrift_unpack((const uint8_t *)buf.ptr, (uint32_t *)&footer_len, &file_meta_data);

		if (file_meta_data.__isset.encryption_algorithm) {
			throw runtime_error("Encrypted Parquet files are not supported");
		}

		// check if we like this schema
		if (file_meta_data.schema.size() < 2) {
			throw runtime_error("Need at least one column in the file");
		}
		if (file_meta_data.schema[0].num_children != (int32_t)(file_meta_data.schema.size() - 1)) {
			throw runtime_error("Only flat tables are supported (no nesting)");
		}

		// skip the first column its the root and otherwise useless
		for (uint64_t col_idx = 1; col_idx < file_meta_data.schema.size(); col_idx++) {
			auto &s_ele = file_meta_data.schema[col_idx];

			if (!s_ele.__isset.type || s_ele.num_children > 0) {
				throw runtime_error("Only flat tables are supported (no nesting)");
			}
			// if this is REQUIRED, there are no defined levels in file, seems unused
			// if field is REPEATED, no bueno
			if (s_ele.repetition_type != FieldRepetitionType::OPTIONAL) {
				throw runtime_error("Only OPTIONAL fields support");
			}

			names.push_back(s_ele.name);
			SQLType type;
			switch (s_ele.type) {
			case Type::BOOLEAN:
				type = SQLType::BOOLEAN;
				break;
			case Type::INT32:
				type = SQLType::INTEGER;
				break;
			case Type::INT64:
				type = SQLType::BIGINT;
				break;
			case Type::INT96: // always a timestamp?
				type = SQLType::TIMESTAMP;
				break;
			case Type::FLOAT:
				type = SQLType::FLOAT;
				break;
			case Type::DOUBLE:
				type = SQLType::DOUBLE;
				break;
				//			case parquet::format::Type::FIXED_LEN_BYTE_ARRAY: {
				// TODO some decimals yuck
			case Type::BYTE_ARRAY:
				type = SQLType::VARCHAR;
				break;

			default:
				throw NotImplementedException("Invalid type");
				break;
			}

			return_types.push_back(type);
			res->sql_types.push_back(type);
		}
		res->group_offset = 0;
		res->current_group = -1;
		res->column_data.resize(return_types.size());
		res->finished = false;
		return move(res);
	}

	template <class T>
	static void _fill_from_dict(ParquetScanColumnData &col_data, idx_t count, Vector &target, idx_t target_offset) {
		for (idx_t i = 0; i < count; i++) {
			if (col_data.defined_buf.ptr[i]) {
				auto offset = col_data.offset_buf.read<int32_t>();
				if (offset > col_data.dict_size) {
					throw runtime_error("Offset " + to_string(offset) + " greater than dictionary size " +
					                    to_string(col_data.dict_size) + " at " + to_string(i + target_offset) +
					                    ". Corrupt file?");
				}
				((T *)FlatVector::GetData(target))[i + target_offset] = ((const T *)col_data.dict.ptr)[offset];
			} else {
				FlatVector::SetNull(target, i + target_offset, true);
			}
		}
	}

	template <class T>
	static void _fill_from_plain(ParquetScanColumnData &col_data, idx_t count, Vector &target, idx_t target_offset) {
		for (idx_t i = 0; i < count; i++) {
			if (col_data.defined_buf.ptr[i]) {
				((T *)FlatVector::GetData(target))[i + target_offset] = col_data.payload.read<T>();
			} else {
				FlatVector::SetNull(target, i + target_offset, true);
			}
		}
	}

	static bool _prepare_page_buffers(ParquetScanFunctionData &data, idx_t col_idx) {
		auto &col_data = data.column_data[col_idx];
		auto &chunk = data.file_meta_data.row_groups[data.current_group].columns[col_idx];

		// clean up a bit to avoid nasty surprises
		col_data.payload.ptr = nullptr;
		col_data.payload.len = 0;
		col_data.dict_decoder = nullptr;
		col_data.defined_decoder = nullptr;

		auto page_header_len = col_data.buf.len;
		if (page_header_len < 1) {
			throw runtime_error("Ran out of bytes to read header from. File corrupt?");
		}
		PageHeader page_hdr;
		thrift_unpack((const uint8_t *)col_data.buf.ptr + col_data.chunk_offset, (uint32_t *)&page_header_len,
		              &page_hdr);

		// the payload starts behind the header, obvsl.
		col_data.buf.inc(page_header_len);

		col_data.payload.len = page_hdr.uncompressed_page_size;

		// handle compression, in the end we expect a pointer to uncompressed parquet data in payload_ptr
		switch (chunk.meta_data.codec) {
		case CompressionCodec::UNCOMPRESSED:
			col_data.payload.ptr = col_data.buf.ptr;
			break;

		case CompressionCodec::SNAPPY: {
			size_t decompressed_size;

			snappy::GetUncompressedLength(col_data.buf.ptr, page_hdr.compressed_page_size, &decompressed_size);
			col_data.decompressed_buf.resize(decompressed_size);

			auto res =
			    snappy::RawUncompress(col_data.buf.ptr, page_hdr.compressed_page_size, col_data.decompressed_buf.ptr);
			if (!res) {
				throw runtime_error("Decompression failure");
			}
			col_data.payload.ptr = col_data.decompressed_buf.ptr;
			break;
		}
		default:
			throw runtime_error("Unsupported compression codec. Try uncompressed or snappy");
		}
		col_data.buf.inc(page_hdr.compressed_page_size);

		// handle page contents
		switch (page_hdr.type) {
		case PageType::DICTIONARY_PAGE: {
			// fill the dictionary vector

			if (page_hdr.__isset.data_page_header || !page_hdr.__isset.dictionary_page_header) {
				throw runtime_error("Dictionary page header mismatch");
			}

			// make sure we like the encoding
			switch (page_hdr.dictionary_page_header.encoding) {
			case Encoding::PLAIN:
			case Encoding::PLAIN_DICTIONARY: // deprecated
				break;

			default:
				throw runtime_error("Dictionary page has unsupported/invalid encoding");
			}

			col_data.dict_size = page_hdr.dictionary_page_header.num_values;
			auto dict_byte_size = col_data.dict_size * GetTypeIdSize(GetInternalType(data.sql_types[col_idx]));

			col_data.dict.resize(dict_byte_size);

			switch (data.sql_types[col_idx].id) {
			case SQLTypeId::BOOLEAN:
			case SQLTypeId::INTEGER:
			case SQLTypeId::BIGINT:
			case SQLTypeId::FLOAT:
			case SQLTypeId::DOUBLE:
				col_data.payload.available(dict_byte_size);
				// TODO this copy could be avoided if we use different buffers for dicts
				col_data.payload.copy_to(col_data.dict.ptr, dict_byte_size);
				break;
			case SQLTypeId::TIMESTAMP:
				col_data.payload.available(dict_byte_size);
				// immediately convert timestamps to duckdb format, potentially fewer conversions
				for (idx_t dict_index = 0; dict_index < col_data.dict_size; dict_index++) {
					auto impala_ns = impala_timestamp_to_nanoseconds(((Int96 *)col_data.payload.ptr)[dict_index]);

					auto ms = impala_ns / 1000000; // nanoseconds
					auto ms_per_day = (int64_t)60 * 60 * 24 * 1000;
					date_t date = Date::EpochToDate(ms / 1000);
					dtime_t time = (dtime_t)(ms % ms_per_day);
					((timestamp_t *)col_data.dict.ptr)[dict_index] = Timestamp::FromDatetime(date, time);
				}

				break;
			case SQLTypeId::VARCHAR: {
				// strings we directly fill a string heap that we can use for the vectors later
				col_data.string_collection = make_unique<ChunkCollection>();

				// we hand-roll a chunk collection to avoid copying strings
				auto append_chunk = make_unique<DataChunk>();
				vector<TypeId> types = {TypeId::VARCHAR};
				col_data.string_collection->types = types;
				append_chunk->Initialize(types);

				for (idx_t dict_index = 0; dict_index < col_data.dict_size; dict_index++) {
					uint32_t str_len = col_data.payload.read<uint32_t>();
					col_data.payload.available(str_len);

					if (append_chunk->size() == STANDARD_VECTOR_SIZE) {
						col_data.string_collection->count += append_chunk->size();
						col_data.string_collection->chunks.push_back(move(append_chunk));
						append_chunk = make_unique<DataChunk>();
						append_chunk->Initialize(types);
					}

					auto utf_type = Utf8Proc::Analyze(col_data.payload.ptr, str_len);
					switch (utf_type) {
					case UnicodeType::ASCII:
						FlatVector::GetData<string_t>(append_chunk->data[0])[append_chunk->size()] =
						    StringVector::AddString(append_chunk->data[0], col_data.payload.ptr, str_len);
						break;
					case UnicodeType::UNICODE:
						// this regrettably copies to normalize
						FlatVector::GetData<string_t>(append_chunk->data[0])[append_chunk->size()] =
						    StringVector::AddString(append_chunk->data[0],
						                            Utf8Proc::Normalize(string(col_data.payload.ptr, str_len)));

						break;
					case UnicodeType::INVALID:
						throw runtime_error("invalid string encoding");
					}

					append_chunk->SetCardinality(append_chunk->size() + 1);
					col_data.payload.inc(str_len);
				}
				// FLUSH last chunk!
				if (append_chunk->size() > 0) {
					col_data.string_collection->count += append_chunk->size();
					col_data.string_collection->chunks.push_back(move(append_chunk));
				}
				col_data.string_collection->Verify();
			} break;
			default:
				throw runtime_error(SQLTypeToString(data.sql_types[col_idx]));
			}
			// important, move to next page which should be a data page
			return false;
		}
		case PageType::DATA_PAGE: {
			if (!page_hdr.__isset.data_page_header || page_hdr.__isset.dictionary_page_header) {
				throw runtime_error("Data page header mismatch");
			}

			if (page_hdr.__isset.data_page_header_v2) {
				throw runtime_error("v2 data page format is not supported");
			}

			col_data.page_value_count = page_hdr.data_page_header.num_values;
			col_data.page_encoding = page_hdr.data_page_header.encoding;

			// we have to first decode the define levels
			switch (page_hdr.data_page_header.definition_level_encoding) {
			case Encoding::RLE: {
				// read length of define payload, always
				uint32_t def_length = col_data.payload.read<uint32_t>();
				col_data.payload.available(def_length);
				col_data.defined_decoder =
				    make_unique<RleBpDecoder>((const uint8_t *)col_data.payload.ptr, def_length, 1);
				col_data.payload.inc(def_length);
			} break;
			default:
				throw runtime_error("Definition levels have unsupported/invalid encoding");
			}

			switch (page_hdr.data_page_header.encoding) {
			case Encoding::RLE_DICTIONARY:
			case Encoding::PLAIN_DICTIONARY: {
				auto enc_length = col_data.payload.read<uint8_t>();
				col_data.dict_decoder =
				    make_unique<RleBpDecoder>((const uint8_t *)col_data.payload.ptr, col_data.payload.len, enc_length);
				break;
			}
			case Encoding::PLAIN:
				// nothing here, see below
				break;

			default:
				throw runtime_error("Data page has unsupported/invalid encoding");
			}

			break;
		}
		case PageType::DATA_PAGE_V2:
			throw runtime_error("v2 data page format is not supported");

		default:
			break; // ignore INDEX page type and any other custom extensions
		}
		return true;
	}

	static void _prepare_chunk_buffer(ParquetScanFunctionData &data, idx_t col_idx) {
		auto &chunk = data.file_meta_data.row_groups[data.current_group].columns[col_idx];
		if (chunk.__isset.file_path) {
			throw runtime_error("Only inlined data files are supported (no references)");
		}

		if (chunk.meta_data.path_in_schema.size() != 1) {
			throw runtime_error("Only flat tables are supported (no nesting)");
		}

		// ugh. sometimes there is an extra offset for the dict. sometimes it's wrong.
		auto chunk_start = chunk.meta_data.data_page_offset;
		if (chunk.meta_data.__isset.dictionary_page_offset && chunk.meta_data.dictionary_page_offset >= 4) {
			// this assumes the data pages follow the dict pages directly.
			chunk_start = chunk.meta_data.dictionary_page_offset;
		}
		auto chunk_len = chunk.meta_data.total_compressed_size;

		// read entire chunk into RAM
		data.pfile.seekg(chunk_start);
		data.column_data[col_idx].buf.resize(chunk_len);
		data.pfile.read(data.column_data[col_idx].buf.ptr, chunk_len);
		if (!data.pfile) {
			throw runtime_error("Could not read chunk. File corrupt?");
		}
	}

	static void parquet_scan_function(ClientContext &context, vector<Value> &input, DataChunk &output,
	                                  FunctionData *dataptr) {
		auto &data = *((ParquetScanFunctionData *)dataptr);

		if (data.finished) {
			return;
		}

		// see if we have to switch to the next row group in the parquet file
		if (data.current_group < 0 ||
		    data.group_offset >= data.file_meta_data.row_groups[data.current_group].num_rows) {

			data.current_group++;
			data.group_offset = 0;

			if (data.current_group == data.file_meta_data.row_groups.size()) {
				data.finished = true;
				return;
			}

			for (idx_t out_col_idx = 0; out_col_idx < output.column_count(); out_col_idx++) {
				auto file_col_idx = data.column_ids[out_col_idx];

				// this is a special case where we are not interested in the actual contents of the file
				if (file_col_idx == COLUMN_IDENTIFIER_ROW_ID) {
					continue;
				}

				_prepare_chunk_buffer(data, file_col_idx);
				// trigger the reading of a new page below
				data.column_data[file_col_idx].page_value_count = 0;
			}
		}

		auto current_group = data.file_meta_data.row_groups[data.current_group];
		output.SetCardinality(std::min((int64_t)STANDARD_VECTOR_SIZE, current_group.num_rows - data.group_offset));
		assert(output.size() > 0);

		for (idx_t out_col_idx = 0; out_col_idx < output.column_count(); out_col_idx++) {
			auto file_col_idx = data.column_ids[out_col_idx];
			if (file_col_idx == COLUMN_IDENTIFIER_ROW_ID) {
				Value constant_42 = Value::BIGINT(42);
				output.data[out_col_idx].Reference(constant_42);
				continue;
			}

			auto &col_data = data.column_data[file_col_idx];

			// we might need to read multiple pages to fill the data chunk
			idx_t output_offset = 0;
			while (output_offset < output.size()) {
				// do this unpack business only if we run out of stuff from the current page
				if (col_data.page_offset >= col_data.page_value_count) {

					// read dictionaries and data page headers so that we are ready to go for scan
					if (!_prepare_page_buffers(data, file_col_idx)) {
						continue;
					}
					col_data.page_offset = 0;
				}

				auto current_batch_size =
				    std::min(col_data.page_value_count - col_data.page_offset, output.size() - output_offset);

				assert(current_batch_size > 0);

				col_data.defined_buf.resize(current_batch_size);
				col_data.defined_decoder->GetBatch<uint8_t>(col_data.defined_buf.ptr, current_batch_size);

				switch (col_data.page_encoding) {
				case Encoding::RLE_DICTIONARY:
				case Encoding::PLAIN_DICTIONARY: {

					idx_t null_count = 0;
					for (idx_t i = 0; i < current_batch_size; i++) {
						if (!col_data.defined_buf.ptr[i]) {
							null_count++;
						}
					}

					col_data.offset_buf.resize(current_batch_size * sizeof(uint32_t));
					col_data.dict_decoder->GetBatch<uint32_t>(col_data.offset_buf.ptr, current_batch_size - null_count);

					// TODO ensure we had seen a dict page IN THIS CHUNK before getting here

					switch (data.sql_types[file_col_idx].id) {
					case SQLTypeId::BOOLEAN:
						_fill_from_dict<bool>(col_data, current_batch_size, output.data[out_col_idx], output_offset);
						break;
					case SQLTypeId::INTEGER:
						_fill_from_dict<int32_t>(col_data, current_batch_size, output.data[out_col_idx], output_offset);
						break;
					case SQLTypeId::BIGINT:
						_fill_from_dict<int64_t>(col_data, current_batch_size, output.data[out_col_idx], output_offset);
						break;
					case SQLTypeId::FLOAT:
						_fill_from_dict<float>(col_data, current_batch_size, output.data[out_col_idx], output_offset);
						break;
					case SQLTypeId::DOUBLE:
						_fill_from_dict<double>(col_data, current_batch_size, output.data[out_col_idx], output_offset);
						break;
					case SQLTypeId::TIMESTAMP:
						_fill_from_dict<timestamp_t>(col_data, current_batch_size, output.data[out_col_idx],
						                             output_offset);
						break;
					case SQLTypeId::VARCHAR: {
						if (!col_data.string_collection) {
							throw runtime_error("Did not see a dictionary for strings. Corrupt file?");
						}

						// the strings can be anywhere in the collection so just reference it all
						for (auto &chunk : col_data.string_collection->chunks) {
							StringVector::AddHeapReference(output.data[out_col_idx], chunk->data[0]);
						}

						auto out_data_ptr = FlatVector::GetData<string_t>(output.data[out_col_idx]);
						for (idx_t i = 0; i < current_batch_size; i++) {
							if (col_data.defined_buf.ptr[i]) {
								auto offset = col_data.offset_buf.read<int32_t>();
								if (offset >= col_data.string_collection->count) {
									throw runtime_error("string dictionary offset out of bounds");
								}
								auto &chunk = col_data.string_collection->chunks[offset / STANDARD_VECTOR_SIZE];
								auto &vec = chunk->data[0];

								out_data_ptr[i + output_offset] =
								    FlatVector::GetData<string_t>(vec)[offset % STANDARD_VECTOR_SIZE];
							} else {
								FlatVector::SetNull(output.data[out_col_idx], i + output_offset, true);
							}
						}
					} break;
					default:
						throw runtime_error(SQLTypeToString(data.sql_types[file_col_idx]));
					}

					break;
				}
				case Encoding::PLAIN:
					assert(col_data.payload.ptr);
					switch (data.sql_types[file_col_idx].id) {
					case SQLTypeId::BOOLEAN: {
						// bit packed this
						auto target_ptr = FlatVector::GetData<bool>(output.data[out_col_idx]);
						int byte_pos = 0;
						for (int32_t i = 0; i < current_batch_size; i++) {
							if (!col_data.defined_buf.ptr[i]) {
								FlatVector::SetNull(output.data[out_col_idx], i + output_offset, true);
								continue;
							}
							col_data.payload.available(1);
							target_ptr[i + output_offset] = (*col_data.payload.ptr >> byte_pos) & 1;
							byte_pos++;
							if (byte_pos == 8) {
								byte_pos = 0;
								col_data.payload.inc(1);
							}
						}
						break;
					}
					case SQLTypeId::INTEGER:
						_fill_from_plain<int32_t>(col_data, current_batch_size, output.data[out_col_idx],
						                          output_offset);
						break;
					case SQLTypeId::BIGINT:
						_fill_from_plain<int64_t>(col_data, current_batch_size, output.data[out_col_idx],
						                          output_offset);
						break;
					case SQLTypeId::FLOAT:
						_fill_from_plain<float>(col_data, current_batch_size, output.data[out_col_idx], output_offset);
						break;
					case SQLTypeId::DOUBLE:
						_fill_from_plain<double>(col_data, current_batch_size, output.data[out_col_idx], output_offset);
						break;
					case SQLTypeId::VARCHAR: {
						for (idx_t i = 0; i < current_batch_size; i++) {
							if (col_data.defined_buf.ptr[i]) {
								uint32_t str_len = col_data.payload.read<uint32_t>();
								col_data.payload.available(str_len);
								FlatVector::GetData<string_t>(output.data[out_col_idx])[i + output_offset] =
								    StringVector::AddString(output.data[out_col_idx], col_data.payload.ptr, str_len);
								col_data.payload.inc(str_len);
							} else {
								FlatVector::SetNull(output.data[out_col_idx], i + output_offset, true);
							}
						}
						break;
					}
					default:
						throw runtime_error(SQLTypeToString(data.sql_types[file_col_idx]));
					}

					break;

				default:
					throw runtime_error("Data page has unsupported/invalid encoding");
				}

				output_offset += current_batch_size;
				col_data.page_offset += current_batch_size;
			}
		}
		data.group_offset += output.size();
	}
};

void ParquetExtension::Load(DuckDB &db) {
	ParquetScanFunction scan_fun;
	CreateTableFunctionInfo info(scan_fun, true);

	Connection conn(db);
	conn.context->transaction.BeginTransaction();
	conn.context->catalog.CreateTableFunction(*conn.context, &info);
	info.name = "read_parquet"; // ok we will have this alias
	conn.context->catalog.CreateTableFunction(*conn.context, &info);

	conn.context->transaction.Commit();
}

// Copyright 2011 Google Inc. All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Various stubs for the open-source version of Snappy.

#ifndef THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_STUBS_INTERNAL_H_
#define THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_STUBS_INTERNAL_H_

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <string>

#include <assert.h>
#include <stdlib.h>
#include <string.h>

#ifdef HAVE_SYS_MMAN_H
#include <sys/mman.h>
#endif

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#if defined(_MSC_VER)
#include <intrin.h>
#endif  // defined(_MSC_VER)

#ifndef __has_feature
#define __has_feature(x) 0
#endif

#if __has_feature(memory_sanitizer)
#include <sanitizer/msan_interface.h>
#define SNAPPY_ANNOTATE_MEMORY_IS_INITIALIZED(address, size) \
    __msan_unpoison((address), (size))
#else
#define SNAPPY_ANNOTATE_MEMORY_IS_INITIALIZED(address, size) /* empty */
#endif  // __has_feature(memory_sanitizer)

// #include "snappy-stubs-public.h"

#if defined(__x86_64__)

// Enable 64-bit optimized versions of some routines.
#define ARCH_K8 1

#elif defined(__ppc64__)

#define ARCH_PPC 1

#elif defined(__aarch64__)

#define ARCH_ARM 1

#endif

// Needed by OS X, among others.
#ifndef MAP_ANONYMOUS
#define MAP_ANONYMOUS MAP_ANON
#endif

// The size of an array, if known at compile-time.
// Will give unexpected results if used on a pointer.
// We undefine it first, since some compilers already have a definition.
#ifdef ARRAYSIZE
#undef ARRAYSIZE
#endif
#define ARRAYSIZE(a) (sizeof(a) / sizeof(*(a)))

// Static prediction hints.
#ifdef HAVE_BUILTIN_EXPECT
#define SNAPPY_PREDICT_FALSE(x) (__builtin_expect(x, 0))
#define SNAPPY_PREDICT_TRUE(x) (__builtin_expect(!!(x), 1))
#else
#define SNAPPY_PREDICT_FALSE(x) x
#define SNAPPY_PREDICT_TRUE(x) x
#endif

// This is only used for recomputing the tag byte table used during
// decompression; for simplicity we just remove it from the open-source
// version (anyone who wants to regenerate it can just do the call
// themselves within main()).
#define DEFINE_bool(flag_name, default_value, description) \
  bool FLAGS_ ## flag_name = default_value
#define DECLARE_bool(flag_name) \
  extern bool FLAGS_ ## flag_name

namespace snappy {

static const uint32 kuint32max = static_cast<uint32>(0xFFFFFFFF);
//static const int64 kint64max = static_cast<int64>(0x7FFFFFFFFFFFFFFFLL);


// HM: Always use aligned load to keep ourselves out of trouble. Sorry.

inline uint16 UNALIGNED_LOAD16(const void *p) {
  uint16 t;
  memcpy(&t, p, sizeof t);
  return t;
}

inline uint32 UNALIGNED_LOAD32(const void *p) {
  uint32 t;
  memcpy(&t, p, sizeof t);
  return t;
}

inline uint64 UNALIGNED_LOAD64(const void *p) {
  uint64 t;
  memcpy(&t, p, sizeof t);
  return t;
}

inline void UNALIGNED_STORE16(void *p, uint16 v) {
  memcpy(p, &v, sizeof v);
}

inline void UNALIGNED_STORE32(void *p, uint32 v) {
  memcpy(p, &v, sizeof v);
}

inline void UNALIGNED_STORE64(void *p, uint64 v) {
  memcpy(p, &v, sizeof v);
}


// The following guarantees declaration of the byte swap functions.
#if defined(SNAPPY_IS_BIG_ENDIAN)

#ifdef HAVE_SYS_BYTEORDER_H
#include <sys/byteorder.h>
#endif

#ifdef HAVE_SYS_ENDIAN_H
#include <sys/endian.h>
#endif

#ifdef _MSC_VER
#include <stdlib.h>
#define bswap_16(x) _byteswap_ushort(x)
#define bswap_32(x) _byteswap_ulong(x)
#define bswap_64(x) _byteswap_uint64(x)

#elif defined(__APPLE__)
// Mac OS X / Darwin features
#include <libkern/OSByteOrder.h>
#define bswap_16(x) OSSwapInt16(x)
#define bswap_32(x) OSSwapInt32(x)
#define bswap_64(x) OSSwapInt64(x)

#elif defined(HAVE_BYTESWAP_H)
#include <byteswap.h>

#elif defined(bswap32)
// FreeBSD defines bswap{16,32,64} in <sys/endian.h> (already #included).
#define bswap_16(x) bswap16(x)
#define bswap_32(x) bswap32(x)
#define bswap_64(x) bswap64(x)

#elif defined(BSWAP_64)
// Solaris 10 defines BSWAP_{16,32,64} in <sys/byteorder.h> (already #included).
#define bswap_16(x) BSWAP_16(x)
#define bswap_32(x) BSWAP_32(x)
#define bswap_64(x) BSWAP_64(x)

#else

inline uint16 bswap_16(uint16 x) {
  return (x << 8) | (x >> 8);
}

inline uint32 bswap_32(uint32 x) {
  x = ((x & 0xff00ff00UL) >> 8) | ((x & 0x00ff00ffUL) << 8);
  return (x >> 16) | (x << 16);
}

inline uint64 bswap_64(uint64 x) {
  x = ((x & 0xff00ff00ff00ff00ULL) >> 8) | ((x & 0x00ff00ff00ff00ffULL) << 8);
  x = ((x & 0xffff0000ffff0000ULL) >> 16) | ((x & 0x0000ffff0000ffffULL) << 16);
  return (x >> 32) | (x << 32);
}

#endif

#endif  // defined(SNAPPY_IS_BIG_ENDIAN)

// Convert to little-endian storage, opposite of network format.
// Convert x from host to little endian: x = LittleEndian.FromHost(x);
// convert x from little endian to host: x = LittleEndian.ToHost(x);
//
//  Store values into unaligned memory converting to little endian order:
//    LittleEndian.Store16(p, x);
//
//  Load unaligned values stored in little endian converting to host order:
//    x = LittleEndian.Load16(p);
class LittleEndian {
 public:
  // Conversion functions.
#if defined(SNAPPY_IS_BIG_ENDIAN)

  static uint16 FromHost16(uint16 x) { return bswap_16(x); }
  static uint16 ToHost16(uint16 x) { return bswap_16(x); }

  static uint32 FromHost32(uint32 x) { return bswap_32(x); }
  static uint32 ToHost32(uint32 x) { return bswap_32(x); }

  static bool IsLittleEndian() { return false; }

#else  // !defined(SNAPPY_IS_BIG_ENDIAN)

  static uint16 FromHost16(uint16 x) { return x; }
  static uint16 ToHost16(uint16 x) { return x; }

  static uint32 FromHost32(uint32 x) { return x; }
  static uint32 ToHost32(uint32 x) { return x; }

  static bool IsLittleEndian() { return true; }

#endif  // !defined(SNAPPY_IS_BIG_ENDIAN)

  // Functions to do unaligned loads and stores in little-endian order.
  static uint16 Load16(const void *p) {
    return ToHost16(UNALIGNED_LOAD16(p));
  }

  static void Store16(void *p, uint16 v) {
    UNALIGNED_STORE16(p, FromHost16(v));
  }

  static uint32 Load32(const void *p) {
    return ToHost32(UNALIGNED_LOAD32(p));
  }

  static void Store32(void *p, uint32 v) {
    UNALIGNED_STORE32(p, FromHost32(v));
  }
};

// Some bit-manipulation functions.
class Bits {
 public:
  // Return floor(log2(n)) for positive integer n.
  static int Log2FloorNonZero(uint32 n);

  // Return floor(log2(n)) for positive integer n.  Returns -1 iff n == 0.
  static int Log2Floor(uint32 n);

  // Return the first set least / most significant bit, 0-indexed.  Returns an
  // undefined value if n == 0.  FindLSBSetNonZero() is similar to ffs() except
  // that it's 0-indexed.
  static int FindLSBSetNonZero(uint32 n);

#if defined(ARCH_K8) || defined(ARCH_PPC) || defined(ARCH_ARM)
  static int FindLSBSetNonZero64(uint64 n);
#endif  // defined(ARCH_K8) || defined(ARCH_PPC) || defined(ARCH_ARM)

 private:
  // No copying
  Bits(const Bits&);
  void operator=(const Bits&);
};

#ifdef HAVE_BUILTIN_CTZ

inline int Bits::Log2FloorNonZero(uint32 n) {
  assert(n != 0);
  // (31 ^ x) is equivalent to (31 - x) for x in [0, 31]. An easy proof
  // represents subtraction in base 2 and observes that there's no carry.
  //
  // GCC and Clang represent __builtin_clz on x86 as 31 ^ _bit_scan_reverse(x).
  // Using "31 ^" here instead of "31 -" allows the optimizer to strip the
  // function body down to _bit_scan_reverse(x).
  return 31 ^ __builtin_clz(n);
}

inline int Bits::Log2Floor(uint32 n) {
  return (n == 0) ? -1 : Bits::Log2FloorNonZero(n);
}

inline int Bits::FindLSBSetNonZero(uint32 n) {
  assert(n != 0);
  return __builtin_ctz(n);
}

#if defined(ARCH_K8) || defined(ARCH_PPC) || defined(ARCH_ARM)
inline int Bits::FindLSBSetNonZero64(uint64 n) {
  assert(n != 0);
  return __builtin_ctzll(n);
}
#endif  // defined(ARCH_K8) || defined(ARCH_PPC) || defined(ARCH_ARM)

#elif defined(_MSC_VER)

inline int Bits::Log2FloorNonZero(uint32 n) {
  assert(n != 0);
  unsigned long where;
  _BitScanReverse(&where, n);
  return static_cast<int>(where);
}

inline int Bits::Log2Floor(uint32 n) {
  unsigned long where;
  if (_BitScanReverse(&where, n))
    return static_cast<int>(where);
  return -1;
}

inline int Bits::FindLSBSetNonZero(uint32 n) {
  assert(n != 0);
  unsigned long where;
  if (_BitScanForward(&where, n))
    return static_cast<int>(where);
  return 32;
}

#if defined(ARCH_K8) || defined(ARCH_PPC) || defined(ARCH_ARM)
inline int Bits::FindLSBSetNonZero64(uint64 n) {
  assert(n != 0);
  unsigned long where;
  if (_BitScanForward64(&where, n))
    return static_cast<int>(where);
  return 64;
}
#endif  // defined(ARCH_K8) || defined(ARCH_PPC) || defined(ARCH_ARM)

#else  // Portable versions.

inline int Bits::Log2FloorNonZero(uint32 n) {
  assert(n != 0);

  int log = 0;
  uint32 value = n;
  for (int i = 4; i >= 0; --i) {
    int shift = (1 << i);
    uint32 x = value >> shift;
    if (x != 0) {
      value = x;
      log += shift;
    }
  }
  assert(value == 1);
  return log;
}

inline int Bits::Log2Floor(uint32 n) {
  return (n == 0) ? -1 : Bits::Log2FloorNonZero(n);
}

inline int Bits::FindLSBSetNonZero(uint32 n) {
  assert(n != 0);

  int rc = 31;
  for (int i = 4, shift = 1 << 4; i >= 0; --i) {
    const uint32 x = n << shift;
    if (x != 0) {
      n = x;
      rc -= shift;
    }
    shift >>= 1;
  }
  return rc;
}

#if defined(ARCH_K8) || defined(ARCH_PPC) || defined(ARCH_ARM)
// FindLSBSetNonZero64() is defined in terms of FindLSBSetNonZero().
inline int Bits::FindLSBSetNonZero64(uint64 n) {
  assert(n != 0);

  const uint32 bottombits = static_cast<uint32>(n);
  if (bottombits == 0) {
    // Bottom bits are zero, so scan in top bits
    return 32 + FindLSBSetNonZero(static_cast<uint32>(n >> 32));
  } else {
    return FindLSBSetNonZero(bottombits);
  }
}
#endif  // defined(ARCH_K8) || defined(ARCH_PPC) || defined(ARCH_ARM)

#endif  // End portable versions.

// Variable-length integer encoding.
class Varint {
 public:
  // Maximum lengths of varint encoding of uint32.
  static const int kMax32 = 5;

  // Attempts to parse a varint32 from a prefix of the bytes in [ptr,limit-1].
  // Never reads a character at or beyond limit.  If a valid/terminated varint32
  // was found in the range, stores it in *OUTPUT and returns a pointer just
  // past the last byte of the varint32. Else returns NULL.  On success,
  // "result <= limit".
  static const char* Parse32WithLimit(const char* ptr, const char* limit,
                                      uint32* OUTPUT);

  // REQUIRES   "ptr" points to a buffer of length sufficient to hold "v".
  // EFFECTS    Encodes "v" into "ptr" and returns a pointer to the
  //            byte just past the last encoded byte.
  static char* Encode32(char* ptr, uint32 v);

  // EFFECTS    Appends the varint representation of "value" to "*s".
  static void Append32(string* s, uint32 value);
};

inline const char* Varint::Parse32WithLimit(const char* p,
                                            const char* l,
                                            uint32* OUTPUT) {
  const unsigned char* ptr = reinterpret_cast<const unsigned char*>(p);
  const unsigned char* limit = reinterpret_cast<const unsigned char*>(l);
  uint32 b, result;
  if (ptr >= limit) return NULL;
  b = *(ptr++); result = b & 127;          if (b < 128) goto done;
  if (ptr >= limit) return NULL;
  b = *(ptr++); result |= (b & 127) <<  7; if (b < 128) goto done;
  if (ptr >= limit) return NULL;
  b = *(ptr++); result |= (b & 127) << 14; if (b < 128) goto done;
  if (ptr >= limit) return NULL;
  b = *(ptr++); result |= (b & 127) << 21; if (b < 128) goto done;
  if (ptr >= limit) return NULL;
  b = *(ptr++); result |= (b & 127) << 28; if (b < 16) goto done;
  return NULL;       // Value is too long to be a varint32
 done:
  *OUTPUT = result;
  return reinterpret_cast<const char*>(ptr);
}

inline char* Varint::Encode32(char* sptr, uint32 v) {
  // Operate on characters as unsigneds
  unsigned char* ptr = reinterpret_cast<unsigned char*>(sptr);
  static const int B = 128;
  if (v < (1<<7)) {
    *(ptr++) = v;
  } else if (v < (1<<14)) {
    *(ptr++) = v | B;
    *(ptr++) = v>>7;
  } else if (v < (1<<21)) {
    *(ptr++) = v | B;
    *(ptr++) = (v>>7) | B;
    *(ptr++) = v>>14;
  } else if (v < (1<<28)) {
    *(ptr++) = v | B;
    *(ptr++) = (v>>7) | B;
    *(ptr++) = (v>>14) | B;
    *(ptr++) = v>>21;
  } else {
    *(ptr++) = v | B;
    *(ptr++) = (v>>7) | B;
    *(ptr++) = (v>>14) | B;
    *(ptr++) = (v>>21) | B;
    *(ptr++) = v>>28;
  }
  return reinterpret_cast<char*>(ptr);
}

// If you know the internal layout of the std::string in use, you can
// replace this function with one that resizes the string without
// filling the new space with zeros (if applicable) --
// it will be non-portable but faster.
inline void STLStringResizeUninitialized(string* s, size_t new_size) {
  s->resize(new_size);
}

// Return a mutable char* pointing to a string's internal buffer,
// which may not be null-terminated. Writing through this pointer will
// modify the string.
//
// string_as_array(&str)[i] is valid for 0 <= i < str.size() until the
// next call to a string method that invalidates iterators.
//
// As of 2006-04, there is no standard-blessed way of getting a
// mutable reference to a string's internal buffer. However, issue 530
// (http://www.open-std.org/JTC1/SC22/WG21/docs/lwg-defects.html#530)
// proposes this as the method. It will officially be part of the standard
// for C++0x. This should already work on all current implementations.
inline char* string_as_array(string* str) {
  return str->empty() ? NULL : &*str->begin();
}

}  // namespace snappy

#endif  // THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_STUBS_INTERNAL_H_

// Copyright 2008 Google Inc. All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Internals shared between the Snappy implementation and its unittest.

#ifndef THIRD_PARTY_SNAPPY_SNAPPY_INTERNAL_H_
#define THIRD_PARTY_SNAPPY_SNAPPY_INTERNAL_H_

// #include "snappy-stubs-internal.h"

namespace snappy {
namespace internal {

// Working memory performs a single allocation to hold all scratch space
// required for compression.
class WorkingMemory {
 public:
  explicit WorkingMemory(size_t input_size);
  ~WorkingMemory();

  // Allocates and clears a hash table using memory in "*this",
  // stores the number of buckets in "*table_size" and returns a pointer to
  // the base of the hash table.
  uint16* GetHashTable(size_t fragment_size, int* table_size) const;
  char* GetScratchInput() const { return input_; }
  char* GetScratchOutput() const { return output_; }

 private:
  char* mem_;      // the allocated memory, never nullptr
  size_t size_;    // the size of the allocated memory, never 0
  uint16* table_;  // the pointer to the hashtable
  char* input_;    // the pointer to the input scratch buffer
  char* output_;   // the pointer to the output scratch buffer

  // No copying
  WorkingMemory(const WorkingMemory&);
  void operator=(const WorkingMemory&);
};

// Flat array compression that does not emit the "uncompressed length"
// prefix. Compresses "input" string to the "*op" buffer.
//
// REQUIRES: "input_length <= kBlockSize"
// REQUIRES: "op" points to an array of memory that is at least
// "MaxCompressedLength(input_length)" in size.
// REQUIRES: All elements in "table[0..table_size-1]" are initialized to zero.
// REQUIRES: "table_size" is a power of two
//
// Returns an "end" pointer into "op" buffer.
// "end - op" is the compressed size of "input".
char* CompressFragment(const char* input,
                       size_t input_length,
                       char* op,
                       uint16* table,
                       const int table_size);

// Find the largest n such that
//
//   s1[0,n-1] == s2[0,n-1]
//   and n <= (s2_limit - s2).
//
// Return make_pair(n, n < 8).
// Does not read *s2_limit or beyond.
// Does not read *(s1 + (s2_limit - s2)) or beyond.
// Requires that s2_limit >= s2.
//
// Separate implementation for 64-bit, little-endian cpus.
#if !defined(SNAPPY_IS_BIG_ENDIAN) && \
    (defined(ARCH_K8) || defined(ARCH_PPC) || defined(ARCH_ARM))
static inline std::pair<size_t, bool> FindMatchLength(const char* s1,
                                                      const char* s2,
                                                      const char* s2_limit) {
  assert(s2_limit >= s2);
  size_t matched = 0;

  // This block isn't necessary for correctness; we could just start looping
  // immediately.  As an optimization though, it is useful.  It creates some not
  // uncommon code paths that determine, without extra effort, whether the match
  // length is less than 8.  In short, we are hoping to avoid a conditional
  // branch, and perhaps get better code layout from the C++ compiler.
  if (SNAPPY_PREDICT_TRUE(s2 <= s2_limit - 8)) {
    uint64 a1 = UNALIGNED_LOAD64(s1);
    uint64 a2 = UNALIGNED_LOAD64(s2);
    if (a1 != a2) {
      return std::pair<size_t, bool>(Bits::FindLSBSetNonZero64(a1 ^ a2) >> 3,
                                     true);
    } else {
      matched = 8;
      s2 += 8;
    }
  }

  // Find out how long the match is. We loop over the data 64 bits at a
  // time until we find a 64-bit block that doesn't match; then we find
  // the first non-matching bit and use that to calculate the total
  // length of the match.
  while (SNAPPY_PREDICT_TRUE(s2 <= s2_limit - 8)) {
    if (UNALIGNED_LOAD64(s2) == UNALIGNED_LOAD64(s1 + matched)) {
      s2 += 8;
      matched += 8;
    } else {
      uint64 x = UNALIGNED_LOAD64(s2) ^ UNALIGNED_LOAD64(s1 + matched);
      int matching_bits = Bits::FindLSBSetNonZero64(x);
      matched += matching_bits >> 3;
      assert(matched >= 8);
      return std::pair<size_t, bool>(matched, false);
    }
  }
  while (SNAPPY_PREDICT_TRUE(s2 < s2_limit)) {
    if (s1[matched] == *s2) {
      ++s2;
      ++matched;
    } else {
      return std::pair<size_t, bool>(matched, matched < 8);
    }
  }
  return std::pair<size_t, bool>(matched, matched < 8);
}
#else
static inline std::pair<size_t, bool> FindMatchLength(const char* s1,
                                                      const char* s2,
                                                      const char* s2_limit) {
  // Implementation based on the x86-64 version, above.
  assert(s2_limit >= s2);
  int matched = 0;

  while (s2 <= s2_limit - 4 &&
         UNALIGNED_LOAD32(s2) == UNALIGNED_LOAD32(s1 + matched)) {
    s2 += 4;
    matched += 4;
  }
  if (LittleEndian::IsLittleEndian() && s2 <= s2_limit - 4) {
    uint32 x = UNALIGNED_LOAD32(s2) ^ UNALIGNED_LOAD32(s1 + matched);
    int matching_bits = Bits::FindLSBSetNonZero(x);
    matched += matching_bits >> 3;
  } else {
    while ((s2 < s2_limit) && (s1[matched] == *s2)) {
      ++s2;
      ++matched;
    }
  }
  return std::pair<size_t, bool>(matched, matched < 8);
}
#endif

// Lookup tables for decompression code.  Give --snappy_dump_decompression_table
// to the unit test to recompute char_table.

enum {
  LITERAL = 0,
  COPY_1_BYTE_OFFSET = 1,  // 3 bit length + 3 bits of offset in opcode
  COPY_2_BYTE_OFFSET = 2,
  COPY_4_BYTE_OFFSET = 3
};
static const int kMaximumTagLength = 5;  // COPY_4_BYTE_OFFSET plus the actual offset.

// Data stored per entry in lookup table:
//      Range   Bits-used       Description
//      ------------------------------------
//      1..64   0..7            Literal/copy length encoded in opcode byte
//      0..7    8..10           Copy offset encoded in opcode byte / 256
//      0..4    11..13          Extra bytes after opcode
//
// We use eight bits for the length even though 7 would have sufficed
// because of efficiency reasons:
//      (1) Extracting a byte is faster than a bit-field
//      (2) It properly aligns copy offset so we do not need a <<8
static const uint16 char_table[256] = {
  0x0001, 0x0804, 0x1001, 0x2001, 0x0002, 0x0805, 0x1002, 0x2002,
  0x0003, 0x0806, 0x1003, 0x2003, 0x0004, 0x0807, 0x1004, 0x2004,
  0x0005, 0x0808, 0x1005, 0x2005, 0x0006, 0x0809, 0x1006, 0x2006,
  0x0007, 0x080a, 0x1007, 0x2007, 0x0008, 0x080b, 0x1008, 0x2008,
  0x0009, 0x0904, 0x1009, 0x2009, 0x000a, 0x0905, 0x100a, 0x200a,
  0x000b, 0x0906, 0x100b, 0x200b, 0x000c, 0x0907, 0x100c, 0x200c,
  0x000d, 0x0908, 0x100d, 0x200d, 0x000e, 0x0909, 0x100e, 0x200e,
  0x000f, 0x090a, 0x100f, 0x200f, 0x0010, 0x090b, 0x1010, 0x2010,
  0x0011, 0x0a04, 0x1011, 0x2011, 0x0012, 0x0a05, 0x1012, 0x2012,
  0x0013, 0x0a06, 0x1013, 0x2013, 0x0014, 0x0a07, 0x1014, 0x2014,
  0x0015, 0x0a08, 0x1015, 0x2015, 0x0016, 0x0a09, 0x1016, 0x2016,
  0x0017, 0x0a0a, 0x1017, 0x2017, 0x0018, 0x0a0b, 0x1018, 0x2018,
  0x0019, 0x0b04, 0x1019, 0x2019, 0x001a, 0x0b05, 0x101a, 0x201a,
  0x001b, 0x0b06, 0x101b, 0x201b, 0x001c, 0x0b07, 0x101c, 0x201c,
  0x001d, 0x0b08, 0x101d, 0x201d, 0x001e, 0x0b09, 0x101e, 0x201e,
  0x001f, 0x0b0a, 0x101f, 0x201f, 0x0020, 0x0b0b, 0x1020, 0x2020,
  0x0021, 0x0c04, 0x1021, 0x2021, 0x0022, 0x0c05, 0x1022, 0x2022,
  0x0023, 0x0c06, 0x1023, 0x2023, 0x0024, 0x0c07, 0x1024, 0x2024,
  0x0025, 0x0c08, 0x1025, 0x2025, 0x0026, 0x0c09, 0x1026, 0x2026,
  0x0027, 0x0c0a, 0x1027, 0x2027, 0x0028, 0x0c0b, 0x1028, 0x2028,
  0x0029, 0x0d04, 0x1029, 0x2029, 0x002a, 0x0d05, 0x102a, 0x202a,
  0x002b, 0x0d06, 0x102b, 0x202b, 0x002c, 0x0d07, 0x102c, 0x202c,
  0x002d, 0x0d08, 0x102d, 0x202d, 0x002e, 0x0d09, 0x102e, 0x202e,
  0x002f, 0x0d0a, 0x102f, 0x202f, 0x0030, 0x0d0b, 0x1030, 0x2030,
  0x0031, 0x0e04, 0x1031, 0x2031, 0x0032, 0x0e05, 0x1032, 0x2032,
  0x0033, 0x0e06, 0x1033, 0x2033, 0x0034, 0x0e07, 0x1034, 0x2034,
  0x0035, 0x0e08, 0x1035, 0x2035, 0x0036, 0x0e09, 0x1036, 0x2036,
  0x0037, 0x0e0a, 0x1037, 0x2037, 0x0038, 0x0e0b, 0x1038, 0x2038,
  0x0039, 0x0f04, 0x1039, 0x2039, 0x003a, 0x0f05, 0x103a, 0x203a,
  0x003b, 0x0f06, 0x103b, 0x203b, 0x003c, 0x0f07, 0x103c, 0x203c,
  0x0801, 0x0f08, 0x103d, 0x203d, 0x1001, 0x0f09, 0x103e, 0x203e,
  0x1801, 0x0f0a, 0x103f, 0x203f, 0x2001, 0x0f0b, 0x1040, 0x2040
};

}  // end namespace internal


// The size of a compression block. Note that many parts of the compression
// code assumes that kBlockSize <= 65536; in particular, the hash table
// can only store 16-bit offsets, and EmitCopy() also assumes the offset
// is 65535 bytes or less. Note also that if you change this, it will
// affect the framing format (see framing_format.txt).
//
// Note that there might be older data around that is compressed with larger
// block sizes, so the decompression code should not rely on the
// non-existence of long backreferences.
static const int kBlockLog = 16;
static const size_t kBlockSize = 1 << kBlockLog;

static const int kMaxHashTableBits = 14;
static const size_t kMaxHashTableSize = 1 << kMaxHashTableBits;


}  // end namespace snappy

#endif  // THIRD_PARTY_SNAPPY_SNAPPY_INTERNAL_H_

// Copyright 2011 Google Inc. All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#ifndef THIRD_PARTY_SNAPPY_SNAPPY_SINKSOURCE_H_
#define THIRD_PARTY_SNAPPY_SNAPPY_SINKSOURCE_H_

#include <stddef.h>

namespace snappy {

// A Sink is an interface that consumes a sequence of bytes.
class Sink {
 public:
  Sink() { }
  virtual ~Sink();

  // Append "bytes[0,n-1]" to this.
  virtual void Append(const char* bytes, size_t n) = 0;

  // Returns a writable buffer of the specified length for appending.
  // May return a pointer to the caller-owned scratch buffer which
  // must have at least the indicated length.  The returned buffer is
  // only valid until the next operation on this Sink.
  //
  // After writing at most "length" bytes, call Append() with the
  // pointer returned from this function and the number of bytes
  // written.  Many Append() implementations will avoid copying
  // bytes if this function returned an internal buffer.
  //
  // If a non-scratch buffer is returned, the caller may only pass a
  // prefix of it to Append().  That is, it is not correct to pass an
  // interior pointer of the returned array to Append().
  //
  // The default implementation always returns the scratch buffer.
  virtual char* GetAppendBuffer(size_t length, char* scratch);

  // For higher performance, Sink implementations can provide custom
  // AppendAndTakeOwnership() and GetAppendBufferVariable() methods.
  // These methods can reduce the number of copies done during
  // compression/decompression.

  // Append "bytes[0,n-1] to the sink. Takes ownership of "bytes"
  // and calls the deleter function as (*deleter)(deleter_arg, bytes, n)
  // to free the buffer. deleter function must be non NULL.
  //
  // The default implementation just calls Append and frees "bytes".
  // Other implementations may avoid a copy while appending the buffer.
  virtual void AppendAndTakeOwnership(
      char* bytes, size_t n, void (*deleter)(void*, const char*, size_t),
      void *deleter_arg);

  // Returns a writable buffer for appending and writes the buffer's capacity to
  // *allocated_size. Guarantees *allocated_size >= min_size.
  // May return a pointer to the caller-owned scratch buffer which must have
  // scratch_size >= min_size.
  //
  // The returned buffer is only valid until the next operation
  // on this ByteSink.
  //
  // After writing at most *allocated_size bytes, call Append() with the
  // pointer returned from this function and the number of bytes written.
  // Many Append() implementations will avoid copying bytes if this function
  // returned an internal buffer.
  //
  // If the sink implementation allocates or reallocates an internal buffer,
  // it should use the desired_size_hint if appropriate. If a caller cannot
  // provide a reasonable guess at the desired capacity, it should set
  // desired_size_hint = 0.
  //
  // If a non-scratch buffer is returned, the caller may only pass
  // a prefix to it to Append(). That is, it is not correct to pass an
  // interior pointer to Append().
  //
  // The default implementation always returns the scratch buffer.
  virtual char* GetAppendBufferVariable(
      size_t min_size, size_t desired_size_hint, char* scratch,
      size_t scratch_size, size_t* allocated_size);

 private:
  // No copying
  Sink(const Sink&);
  void operator=(const Sink&);
};

// A Source is an interface that yields a sequence of bytes
class Source {
 public:
  Source() { }
  virtual ~Source();

  // Return the number of bytes left to read from the source
  virtual size_t Available() const = 0;

  // Peek at the next flat region of the source.  Does not reposition
  // the source.  The returned region is empty iff Available()==0.
  //
  // Returns a pointer to the beginning of the region and store its
  // length in *len.
  //
  // The returned region is valid until the next call to Skip() or
  // until this object is destroyed, whichever occurs first.
  //
  // The returned region may be larger than Available() (for example
  // if this ByteSource is a view on a substring of a larger source).
  // The caller is responsible for ensuring that it only reads the
  // Available() bytes.
  virtual const char* Peek(size_t* len) = 0;

  // Skip the next n bytes.  Invalidates any buffer returned by
  // a previous call to Peek().
  // REQUIRES: Available() >= n
  virtual void Skip(size_t n) = 0;

 private:
  // No copying
  Source(const Source&);
  void operator=(const Source&);
};

// A Source implementation that yields the contents of a flat array
class ByteArraySource : public Source {
 public:
  ByteArraySource(const char* p, size_t n) : ptr_(p), left_(n) { }
  virtual ~ByteArraySource();
  virtual size_t Available() const;
  virtual const char* Peek(size_t* len);
  virtual void Skip(size_t n);
 private:
  const char* ptr_;
  size_t left_;
};

// A Sink implementation that writes to a flat array without any bound checks.
class UncheckedByteArraySink : public Sink {
 public:
  explicit UncheckedByteArraySink(char* dest) : dest_(dest) { }
  virtual ~UncheckedByteArraySink();
  virtual void Append(const char* data, size_t n);
  virtual char* GetAppendBuffer(size_t len, char* scratch);
  virtual char* GetAppendBufferVariable(
      size_t min_size, size_t desired_size_hint, char* scratch,
      size_t scratch_size, size_t* allocated_size);
  virtual void AppendAndTakeOwnership(
      char* bytes, size_t n, void (*deleter)(void*, const char*, size_t),
      void *deleter_arg);

  // Return the current output pointer so that a caller can see how
  // many bytes were produced.
  // Note: this is not a Sink method.
  char* CurrentDestination() const { return dest_; }
 private:
  char* dest_;
};

}  // namespace snappy

#endif  // THIRD_PARTY_SNAPPY_SNAPPY_SINKSOURCE_H_

// Copyright 2011 Google Inc. All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include <algorithm>
#include <string>

// #include "snappy-stubs-internal.h"

namespace snappy {

void Varint::Append32(string* s, uint32 value) {
  char buf[Varint::kMax32];
  const char* p = Varint::Encode32(buf, value);
  s->append(buf, p - buf);
}

}  // namespace snappy

// Copyright 2011 Google Inc. All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include <string.h>

// #include "snappy-sinksource.h"

namespace snappy {

Source::~Source() { }

Sink::~Sink() { }

char* Sink::GetAppendBuffer(size_t length, char* scratch) {
  return scratch;
}

char* Sink::GetAppendBufferVariable(
      size_t min_size, size_t desired_size_hint, char* scratch,
      size_t scratch_size, size_t* allocated_size) {
  *allocated_size = scratch_size;
  return scratch;
}

void Sink::AppendAndTakeOwnership(
    char* bytes, size_t n,
    void (*deleter)(void*, const char*, size_t),
    void *deleter_arg) {
  Append(bytes, n);
  (*deleter)(deleter_arg, bytes, n);
}

ByteArraySource::~ByteArraySource() { }

size_t ByteArraySource::Available() const { return left_; }

const char* ByteArraySource::Peek(size_t* len) {
  *len = left_;
  return ptr_;
}

void ByteArraySource::Skip(size_t n) {
  left_ -= n;
  ptr_ += n;
}

UncheckedByteArraySink::~UncheckedByteArraySink() { }

void UncheckedByteArraySink::Append(const char* data, size_t n) {
  // Do no copying if the caller filled in the result of GetAppendBuffer()
  if (data != dest_) {
    memcpy(dest_, data, n);
  }
  dest_ += n;
}

char* UncheckedByteArraySink::GetAppendBuffer(size_t len, char* scratch) {
  return dest_;
}

void UncheckedByteArraySink::AppendAndTakeOwnership(
    char* data, size_t n,
    void (*deleter)(void*, const char*, size_t),
    void *deleter_arg) {
  if (data != dest_) {
    memcpy(dest_, data, n);
    (*deleter)(deleter_arg, data, n);
  }
  dest_ += n;
}

char* UncheckedByteArraySink::GetAppendBufferVariable(
      size_t min_size, size_t desired_size_hint, char* scratch,
      size_t scratch_size, size_t* allocated_size) {
  *allocated_size = desired_size_hint;
  return dest_;
}

}  // namespace snappy

// Copyright 2005 Google Inc. All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// #include "snappy.h"
// #include "snappy-internal.h"
// #include "snappy-sinksource.h"

#if !defined(SNAPPY_HAVE_SSSE3)
// __SSSE3__ is defined by GCC and Clang. Visual Studio doesn't target SIMD
// support between SSE2 and AVX (so SSSE3 instructions require AVX support), and
// defines __AVX__ when AVX support is available.
#if defined(__SSSE3__) || defined(__AVX__)
#define SNAPPY_HAVE_SSSE3 1
#else
#define SNAPPY_HAVE_SSSE3 0
#endif
#endif  // !defined(SNAPPY_HAVE_SSSE3)

#if !defined(SNAPPY_HAVE_BMI2)
// __BMI2__ is defined by GCC and Clang. Visual Studio doesn't target BMI2
// specifically, but it does define __AVX2__ when AVX2 support is available.
// Fortunately, AVX2 was introduced in Haswell, just like BMI2.
//
// BMI2 is not defined as a subset of AVX2 (unlike SSSE3 and AVX above). So,
// GCC and Clang can build code with AVX2 enabled but BMI2 disabled, in which
// case issuing BMI2 instructions results in a compiler error.
#if defined(__BMI2__) || (defined(_MSC_VER) && defined(__AVX2__))
#define SNAPPY_HAVE_BMI2 1
#else
#define SNAPPY_HAVE_BMI2 0
#endif
#endif  // !defined(SNAPPY_HAVE_BMI2)

#if SNAPPY_HAVE_SSSE3
// Please do not replace with <x86intrin.h>. or with headers that assume more
// advanced SSE versions without checking with all the OWNERS.
#include <tmmintrin.h>
#endif

#if SNAPPY_HAVE_BMI2
// Please do not replace with <x86intrin.h>. or with headers that assume more
// advanced SSE versions without checking with all the OWNERS.
#include <immintrin.h>
#endif

#include <stdio.h>

#include <algorithm>
#include <string>
#include <vector>

namespace snappy {

using internal::COPY_1_BYTE_OFFSET;
using internal::COPY_2_BYTE_OFFSET;
using internal::LITERAL;
using internal::char_table;
using internal::kMaximumTagLength;

// Any hash function will produce a valid compressed bitstream, but a good
// hash function reduces the number of collisions and thus yields better
// compression for compressible input, and more speed for incompressible
// input. Of course, it doesn't hurt if the hash function is reasonably fast
// either, as it gets called a lot.
static inline uint32 HashBytes(uint32 bytes, int shift) {
  uint32 kMul = 0x1e35a7bd;
  return (bytes * kMul) >> shift;
}
static inline uint32 Hash(const char* p, int shift) {
  return HashBytes(UNALIGNED_LOAD32(p), shift);
}

size_t MaxCompressedLength(size_t source_len) {
  // Compressed data can be defined as:
  //    compressed := item* literal*
  //    item       := literal* copy
  //
  // The trailing literal sequence has a space blowup of at most 62/60
  // since a literal of length 60 needs one tag byte + one extra byte
  // for length information.
  //
  // Item blowup is trickier to measure.  Suppose the "copy" op copies
  // 4 bytes of data.  Because of a special check in the encoding code,
  // we produce a 4-byte copy only if the offset is < 65536.  Therefore
  // the copy op takes 3 bytes to encode, and this type of item leads
  // to at most the 62/60 blowup for representing literals.
  //
  // Suppose the "copy" op copies 5 bytes of data.  If the offset is big
  // enough, it will take 5 bytes to encode the copy op.  Therefore the
  // worst case here is a one-byte literal followed by a five-byte copy.
  // I.e., 6 bytes of input turn into 7 bytes of "compressed" data.
  //
  // This last factor dominates the blowup, so the final estimate is:
  return 32 + source_len + source_len/6;
}

namespace {

void UnalignedCopy64(const void* src, void* dst) {
  char tmp[8];
  memcpy(tmp, src, 8);
  memcpy(dst, tmp, 8);
}

void UnalignedCopy128(const void* src, void* dst) {
  // memcpy gets vectorized when the appropriate compiler options are used.
  // For example, x86 compilers targeting SSE2+ will optimize to an SSE2 load
  // and store.
  char tmp[16];
  memcpy(tmp, src, 16);
  memcpy(dst, tmp, 16);
}

// Copy [src, src+(op_limit-op)) to [op, (op_limit-op)) a byte at a time. Used
// for handling COPY operations where the input and output regions may overlap.
// For example, suppose:
//    src       == "ab"
//    op        == src + 2
//    op_limit  == op + 20
// After IncrementalCopySlow(src, op, op_limit), the result will have eleven
// copies of "ab"
//    ababababababababababab
// Note that this does not match the semantics of either memcpy() or memmove().
inline char* IncrementalCopySlow(const char* src, char* op,
                                 char* const op_limit) {
  // TODO: Remove pragma when LLVM is aware this
  // function is only called in cold regions and when cold regions don't get
  // vectorized or unrolled.
#ifdef __clang__
#pragma clang loop unroll(disable)
#endif
  while (op < op_limit) {
    *op++ = *src++;
  }
  return op_limit;
}

#if SNAPPY_HAVE_SSSE3

// This is a table of shuffle control masks that can be used as the source
// operand for PSHUFB to permute the contents of the destination XMM register
// into a repeating byte pattern.
alignas(16) const char pshufb_fill_patterns[7][16] = {
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1},
  {0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0},
  {0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3},
  {0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0},
  {0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3},
  {0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 0, 1},
};

#endif  // SNAPPY_HAVE_SSSE3

// Copy [src, src+(op_limit-op)) to [op, (op_limit-op)) but faster than
// IncrementalCopySlow. buf_limit is the address past the end of the writable
// region of the buffer.
inline char* IncrementalCopy(const char* src, char* op, char* const op_limit,
                             char* const buf_limit) {
  // Terminology:
  //
  // slop = buf_limit - op
  // pat  = op - src
  // len  = limit - op
  assert(src < op);
  assert(op <= op_limit);
  assert(op_limit <= buf_limit);
  // NOTE: The compressor always emits 4 <= len <= 64. It is ok to assume that
  // to optimize this function but we have to also handle other cases in case
  // the input does not satisfy these conditions.

  size_t pattern_size = op - src;
  // The cases are split into different branches to allow the branch predictor,
  // FDO, and static prediction hints to work better. For each input we list the
  // ratio of invocations that match each condition.
  //
  // input        slop < 16   pat < 8  len > 16
  // ------------------------------------------
  // html|html4|cp   0%         1.01%    27.73%
  // urls            0%         0.88%    14.79%
  // jpg             0%        64.29%     7.14%
  // pdf             0%         2.56%    58.06%
  // txt[1-4]        0%         0.23%     0.97%
  // pb              0%         0.96%    13.88%
  // bin             0.01%     22.27%    41.17%
  //
  // It is very rare that we don't have enough slop for doing block copies. It
  // is also rare that we need to expand a pattern. Small patterns are common
  // for incompressible formats and for those we are plenty fast already.
  // Lengths are normally not greater than 16 but they vary depending on the
  // input. In general if we always predict len <= 16 it would be an ok
  // prediction.
  //
  // In order to be fast we want a pattern >= 8 bytes and an unrolled loop
  // copying 2x 8 bytes at a time.

  // Handle the uncommon case where pattern is less than 8 bytes.
  if (SNAPPY_PREDICT_FALSE(pattern_size < 8)) {
#if SNAPPY_HAVE_SSSE3
    // Load the first eight bytes into an 128-bit XMM register, then use PSHUFB
    // to permute the register's contents in-place into a repeating sequence of
    // the first "pattern_size" bytes.
    // For example, suppose:
    //    src       == "abc"
    //    op        == op + 3
    // After _mm_shuffle_epi8(), "pattern" will have five copies of "abc"
    // followed by one byte of slop: abcabcabcabcabca.
    //
    // The non-SSE fallback implementation suffers from store-forwarding stalls
    // because its loads and stores partly overlap. By expanding the pattern
    // in-place, we avoid the penalty.
    if (SNAPPY_PREDICT_TRUE(op <= buf_limit - 16)) {
      const __m128i shuffle_mask = _mm_load_si128(
          reinterpret_cast<const __m128i*>(pshufb_fill_patterns)
          + pattern_size - 1);
      const __m128i pattern = _mm_shuffle_epi8(
          _mm_loadl_epi64(reinterpret_cast<const __m128i*>(src)), shuffle_mask);
      // Uninitialized bytes are masked out by the shuffle mask.
      // TODO: remove annotation and macro defs once MSan is fixed.
      SNAPPY_ANNOTATE_MEMORY_IS_INITIALIZED(&pattern, sizeof(pattern));
      pattern_size *= 16 / pattern_size;
      char* op_end = std::min(op_limit, buf_limit - 15);
      while (op < op_end) {
        _mm_storeu_si128(reinterpret_cast<__m128i*>(op), pattern);
        op += pattern_size;
      }
      if (SNAPPY_PREDICT_TRUE(op >= op_limit)) return op_limit;
    }
    return IncrementalCopySlow(src, op, op_limit);
#else  // !SNAPPY_HAVE_SSSE3
    // If plenty of buffer space remains, expand the pattern to at least 8
    // bytes. The way the following loop is written, we need 8 bytes of buffer
    // space if pattern_size >= 4, 11 bytes if pattern_size is 1 or 3, and 10
    // bytes if pattern_size is 2.  Precisely encoding that is probably not
    // worthwhile; instead, invoke the slow path if we cannot write 11 bytes
    // (because 11 are required in the worst case).
    if (SNAPPY_PREDICT_TRUE(op <= buf_limit - 11)) {
      while (pattern_size < 8) {
        UnalignedCopy64(src, op);
        op += pattern_size;
        pattern_size *= 2;
      }
      if (SNAPPY_PREDICT_TRUE(op >= op_limit)) return op_limit;
    } else {
      return IncrementalCopySlow(src, op, op_limit);
    }
#endif  // SNAPPY_HAVE_SSSE3
  }
  assert(pattern_size >= 8);

  // Copy 2x 8 bytes at a time. Because op - src can be < 16, a single
  // UnalignedCopy128 might overwrite data in op. UnalignedCopy64 is safe
  // because expanding the pattern to at least 8 bytes guarantees that
  // op - src >= 8.
  //
  // Typically, the op_limit is the gating factor so try to simplify the loop
  // based on that.
  if (SNAPPY_PREDICT_TRUE(op_limit <= buf_limit - 16)) {
    // Factor the displacement from op to the source into a variable. This helps
    // simplify the loop below by only varying the op pointer which we need to
    // test for the end. Note that this was done after carefully examining the
    // generated code to allow the addressing modes in the loop below to
    // maximize micro-op fusion where possible on modern Intel processors. The
    // generated code should be checked carefully for new processors or with
    // major changes to the compiler.
    // TODO: Simplify this code when the compiler reliably produces
    // the correct x86 instruction sequence.
    ptrdiff_t op_to_src = src - op;

    // The trip count of this loop is not large and so unrolling will only hurt
    // code size without helping performance.
    //
    // TODO: Replace with loop trip count hint.
#ifdef __clang__
#pragma clang loop unroll(disable)
#endif
    do {
      UnalignedCopy64(op + op_to_src, op);
      UnalignedCopy64(op + op_to_src + 8, op + 8);
      op += 16;
    } while (op < op_limit);
    return op_limit;
  }

  // Fall back to doing as much as we can with the available slop in the
  // buffer. This code path is relatively cold however so we save code size by
  // avoiding unrolling and vectorizing.
  //
  // TODO: Remove pragma when when cold regions don't get vectorized
  // or unrolled.
#ifdef __clang__
#pragma clang loop unroll(disable)
#endif
  for (char *op_end = buf_limit - 16; op < op_end; op += 16, src += 16) {
    UnalignedCopy64(src, op);
    UnalignedCopy64(src + 8, op + 8);
  }
  if (op >= op_limit)
    return op_limit;

  // We only take this branch if we didn't have enough slop and we can do a
  // single 8 byte copy.
  if (SNAPPY_PREDICT_FALSE(op <= buf_limit - 8)) {
    UnalignedCopy64(src, op);
    src += 8;
    op += 8;
  }
  return IncrementalCopySlow(src, op, op_limit);
}

}  // namespace

template <bool allow_fast_path>
static inline char* EmitLiteral(char* op,
                                const char* literal,
                                int len) {
  // The vast majority of copies are below 16 bytes, for which a
  // call to memcpy is overkill. This fast path can sometimes
  // copy up to 15 bytes too much, but that is okay in the
  // main loop, since we have a bit to go on for both sides:
  //
  //   - The input will always have kInputMarginBytes = 15 extra
  //     available bytes, as long as we're in the main loop, and
  //     if not, allow_fast_path = false.
  //   - The output will always have 32 spare bytes (see
  //     MaxCompressedLength).
  assert(len > 0);      // Zero-length literals are disallowed
  int n = len - 1;
  if (allow_fast_path && len <= 16) {
    // Fits in tag byte
    *op++ = LITERAL | (n << 2);

    UnalignedCopy128(literal, op);
    return op + len;
  }

  if (n < 60) {
    // Fits in tag byte
    *op++ = LITERAL | (n << 2);
  } else {
    int count = (Bits::Log2Floor(n) >> 3) + 1;
    assert(count >= 1);
    assert(count <= 4);
    *op++ = LITERAL | ((59 + count) << 2);
    // Encode in upcoming bytes.
    // Write 4 bytes, though we may care about only 1 of them. The output buffer
    // is guaranteed to have at least 3 more spaces left as 'len >= 61' holds
    // here and there is a memcpy of size 'len' below.
    LittleEndian::Store32(op, n);
    op += count;
  }
  memcpy(op, literal, len);
  return op + len;
}

template <bool len_less_than_12>
static inline char* EmitCopyAtMost64(char* op, size_t offset, size_t len) {
  assert(len <= 64);
  assert(len >= 4);
  assert(offset < 65536);
  assert(len_less_than_12 == (len < 12));

  if (len_less_than_12 && SNAPPY_PREDICT_TRUE(offset < 2048)) {
    // offset fits in 11 bits.  The 3 highest go in the top of the first byte,
    // and the rest go in the second byte.
    *op++ = COPY_1_BYTE_OFFSET + ((len - 4) << 2) + ((offset >> 3) & 0xe0);
    *op++ = offset & 0xff;
  } else {
    // Write 4 bytes, though we only care about 3 of them.  The output buffer
    // is required to have some slack, so the extra byte won't overrun it.
    uint32 u = COPY_2_BYTE_OFFSET + ((len - 1) << 2) + (offset << 8);
    LittleEndian::Store32(op, u);
    op += 3;
  }
  return op;
}

template <bool len_less_than_12>
static inline char* EmitCopy(char* op, size_t offset, size_t len) {
  assert(len_less_than_12 == (len < 12));
  if (len_less_than_12) {
    return EmitCopyAtMost64</*len_less_than_12=*/true>(op, offset, len);
  } else {
    // A special case for len <= 64 might help, but so far measurements suggest
    // it's in the noise.

    // Emit 64 byte copies but make sure to keep at least four bytes reserved.
    while (SNAPPY_PREDICT_FALSE(len >= 68)) {
      op = EmitCopyAtMost64</*len_less_than_12=*/false>(op, offset, 64);
      len -= 64;
    }

    // One or two copies will now finish the job.
    if (len > 64) {
      op = EmitCopyAtMost64</*len_less_than_12=*/false>(op, offset, 60);
      len -= 60;
    }

    // Emit remainder.
    if (len < 12) {
      op = EmitCopyAtMost64</*len_less_than_12=*/true>(op, offset, len);
    } else {
      op = EmitCopyAtMost64</*len_less_than_12=*/false>(op, offset, len);
    }
    return op;
  }
}

bool GetUncompressedLength(const char* start, size_t n, size_t* result) {
  uint32 v = 0;
  const char* limit = start + n;
  if (Varint::Parse32WithLimit(start, limit, &v) != NULL) {
    *result = v;
    return true;
  } else {
    return false;
  }
}

namespace {
uint32 CalculateTableSize(uint32 input_size) {
  assert(kMaxHashTableSize >= 256);
  if (input_size > kMaxHashTableSize) {
    return kMaxHashTableSize;
  }
  if (input_size < 256) {
    return 256;
  }
  // This is equivalent to Log2Ceiling(input_size), assuming input_size > 1.
  // 2 << Log2Floor(x - 1) is equivalent to 1 << (1 + Log2Floor(x - 1)).
  return 2u << Bits::Log2Floor(input_size - 1);
}
}  // namespace

namespace internal {
WorkingMemory::WorkingMemory(size_t input_size) {
  const size_t max_fragment_size = std::min(input_size, kBlockSize);
  const size_t table_size = CalculateTableSize(max_fragment_size);
  size_ = table_size * sizeof(*table_) + max_fragment_size +
          MaxCompressedLength(max_fragment_size);
  mem_ = std::allocator<char>().allocate(size_);
  table_ = reinterpret_cast<uint16*>(mem_);
  input_ = mem_ + table_size * sizeof(*table_);
  output_ = input_ + max_fragment_size;
}

WorkingMemory::~WorkingMemory() {
  std::allocator<char>().deallocate(mem_, size_);
}

uint16* WorkingMemory::GetHashTable(size_t fragment_size,
                                    int* table_size) const {
  const size_t htsize = CalculateTableSize(fragment_size);
  memset(table_, 0, htsize * sizeof(*table_));
  *table_size = htsize;
  return table_;
}
}  // end namespace internal

// For 0 <= offset <= 4, GetUint32AtOffset(GetEightBytesAt(p), offset) will
// equal UNALIGNED_LOAD32(p + offset).  Motivation: On x86-64 hardware we have
// empirically found that overlapping loads such as
//  UNALIGNED_LOAD32(p) ... UNALIGNED_LOAD32(p+1) ... UNALIGNED_LOAD32(p+2)
// are slower than UNALIGNED_LOAD64(p) followed by shifts and casts to uint32.
//
// We have different versions for 64- and 32-bit; ideally we would avoid the
// two functions and just inline the UNALIGNED_LOAD64 call into
// GetUint32AtOffset, but GCC (at least not as of 4.6) is seemingly not clever
// enough to avoid loading the value multiple times then. For 64-bit, the load
// is done when GetEightBytesAt() is called, whereas for 32-bit, the load is
// done at GetUint32AtOffset() time.

#ifdef ARCH_K8

typedef uint64 EightBytesReference;

static inline EightBytesReference GetEightBytesAt(const char* ptr) {
  return UNALIGNED_LOAD64(ptr);
}

static inline uint32 GetUint32AtOffset(uint64 v, int offset) {
  assert(offset >= 0);
  assert(offset <= 4);
  return v >> (LittleEndian::IsLittleEndian() ? 8 * offset : 32 - 8 * offset);
}

#else

typedef const char* EightBytesReference;

static inline EightBytesReference GetEightBytesAt(const char* ptr) {
  return ptr;
}

static inline uint32 GetUint32AtOffset(const char* v, int offset) {
  assert(offset >= 0);
  assert(offset <= 4);
  return UNALIGNED_LOAD32(v + offset);
}

#endif

// Flat array compression that does not emit the "uncompressed length"
// prefix. Compresses "input" string to the "*op" buffer.
//
// REQUIRES: "input" is at most "kBlockSize" bytes long.
// REQUIRES: "op" points to an array of memory that is at least
// "MaxCompressedLength(input.size())" in size.
// REQUIRES: All elements in "table[0..table_size-1]" are initialized to zero.
// REQUIRES: "table_size" is a power of two
//
// Returns an "end" pointer into "op" buffer.
// "end - op" is the compressed size of "input".
namespace internal {
char* CompressFragment(const char* input,
                       size_t input_size,
                       char* op,
                       uint16* table,
                       const int table_size) {
  // "ip" is the input pointer, and "op" is the output pointer.
  const char* ip = input;
  assert(input_size <= kBlockSize);
  assert((table_size & (table_size - 1)) == 0);  // table must be power of two
  const int shift = 32 - Bits::Log2Floor(table_size);
  assert(static_cast<int>(kuint32max >> shift) == table_size - 1);
  const char* ip_end = input + input_size;
  const char* base_ip = ip;
  // Bytes in [next_emit, ip) will be emitted as literal bytes.  Or
  // [next_emit, ip_end) after the main loop.
  const char* next_emit = ip;

  const size_t kInputMarginBytes = 15;
  if (SNAPPY_PREDICT_TRUE(input_size >= kInputMarginBytes)) {
    const char* ip_limit = input + input_size - kInputMarginBytes;

    for (uint32 next_hash = Hash(++ip, shift); ; ) {
      assert(next_emit < ip);
      // The body of this loop calls EmitLiteral once and then EmitCopy one or
      // more times.  (The exception is that when we're close to exhausting
      // the input we goto emit_remainder.)
      //
      // In the first iteration of this loop we're just starting, so
      // there's nothing to copy, so calling EmitLiteral once is
      // necessary.  And we only start a new iteration when the
      // current iteration has determined that a call to EmitLiteral will
      // precede the next call to EmitCopy (if any).
      //
      // Step 1: Scan forward in the input looking for a 4-byte-long match.
      // If we get close to exhausting the input then goto emit_remainder.
      //
      // Heuristic match skipping: If 32 bytes are scanned with no matches
      // found, start looking only at every other byte. If 32 more bytes are
      // scanned (or skipped), look at every third byte, etc.. When a match is
      // found, immediately go back to looking at every byte. This is a small
      // loss (~5% performance, ~0.1% density) for compressible data due to more
      // bookkeeping, but for non-compressible data (such as JPEG) it's a huge
      // win since the compressor quickly "realizes" the data is incompressible
      // and doesn't bother looking for matches everywhere.
      //
      // The "skip" variable keeps track of how many bytes there are since the
      // last match; dividing it by 32 (ie. right-shifting by five) gives the
      // number of bytes to move ahead for each iteration.
      uint32 skip = 32;

      const char* next_ip = ip;
      const char* candidate;
      do {
        ip = next_ip;
        uint32 hash = next_hash;
        assert(hash == Hash(ip, shift));
        uint32 bytes_between_hash_lookups = skip >> 5;
        skip += bytes_between_hash_lookups;
        next_ip = ip + bytes_between_hash_lookups;
        if (SNAPPY_PREDICT_FALSE(next_ip > ip_limit)) {
          goto emit_remainder;
        }
        next_hash = Hash(next_ip, shift);
        candidate = base_ip + table[hash];
        assert(candidate >= base_ip);
        assert(candidate < ip);

        table[hash] = ip - base_ip;
      } while (SNAPPY_PREDICT_TRUE(UNALIGNED_LOAD32(ip) !=
                                 UNALIGNED_LOAD32(candidate)));

      // Step 2: A 4-byte match has been found.  We'll later see if more
      // than 4 bytes match.  But, prior to the match, input
      // bytes [next_emit, ip) are unmatched.  Emit them as "literal bytes."
      assert(next_emit + 16 <= ip_end);
      op = EmitLiteral</*allow_fast_path=*/true>(op, next_emit, ip - next_emit);

      // Step 3: Call EmitCopy, and then see if another EmitCopy could
      // be our next move.  Repeat until we find no match for the
      // input immediately after what was consumed by the last EmitCopy call.
      //
      // If we exit this loop normally then we need to call EmitLiteral next,
      // though we don't yet know how big the literal will be.  We handle that
      // by proceeding to the next iteration of the main loop.  We also can exit
      // this loop via goto if we get close to exhausting the input.
      EightBytesReference input_bytes;
      uint32 candidate_bytes = 0;

      do {
        // We have a 4-byte match at ip, and no need to emit any
        // "literal bytes" prior to ip.
        const char* base = ip;
        std::pair<size_t, bool> p =
            FindMatchLength(candidate + 4, ip + 4, ip_end);
        size_t matched = 4 + p.first;
        ip += matched;
        size_t offset = base - candidate;
        assert(0 == memcmp(base, candidate, matched));
        if (p.second) {
          op = EmitCopy</*len_less_than_12=*/true>(op, offset, matched);
        } else {
          op = EmitCopy</*len_less_than_12=*/false>(op, offset, matched);
        }
        next_emit = ip;
        if (SNAPPY_PREDICT_FALSE(ip >= ip_limit)) {
          goto emit_remainder;
        }
        // We are now looking for a 4-byte match again.  We read
        // table[Hash(ip, shift)] for that.  To improve compression,
        // we also update table[Hash(ip - 1, shift)] and table[Hash(ip, shift)].
        input_bytes = GetEightBytesAt(ip - 1);
        uint32 prev_hash = HashBytes(GetUint32AtOffset(input_bytes, 0), shift);
        table[prev_hash] = ip - base_ip - 1;
        uint32 cur_hash = HashBytes(GetUint32AtOffset(input_bytes, 1), shift);
        candidate = base_ip + table[cur_hash];
        candidate_bytes = UNALIGNED_LOAD32(candidate);
        table[cur_hash] = ip - base_ip;
      } while (GetUint32AtOffset(input_bytes, 1) == candidate_bytes);

      next_hash = HashBytes(GetUint32AtOffset(input_bytes, 2), shift);
      ++ip;
    }
  }

 emit_remainder:
  // Emit the remaining bytes as a literal
  if (next_emit < ip_end) {
    op = EmitLiteral</*allow_fast_path=*/false>(op, next_emit,
                                                ip_end - next_emit);
  }

  return op;
}
}  // end namespace internal

// Called back at avery compression call to trace parameters and sizes.
static inline void Report(const char *algorithm, size_t compressed_size,
                          size_t uncompressed_size) {}

// Signature of output types needed by decompression code.
// The decompression code is templatized on a type that obeys this
// signature so that we do not pay virtual function call overhead in
// the middle of a tight decompression loop.
//
// class DecompressionWriter {
//  public:
//   // Called before decompression
//   void SetExpectedLength(size_t length);
//
//   // Called after decompression
//   bool CheckLength() const;
//
//   // Called repeatedly during decompression
//   bool Append(const char* ip, size_t length);
//   bool AppendFromSelf(uint32 offset, size_t length);
//
//   // The rules for how TryFastAppend differs from Append are somewhat
//   // convoluted:
//   //
//   //  - TryFastAppend is allowed to decline (return false) at any
//   //    time, for any reason -- just "return false" would be
//   //    a perfectly legal implementation of TryFastAppend.
//   //    The intention is for TryFastAppend to allow a fast path
//   //    in the common case of a small append.
//   //  - TryFastAppend is allowed to read up to <available> bytes
//   //    from the input buffer, whereas Append is allowed to read
//   //    <length>. However, if it returns true, it must leave
//   //    at least five (kMaximumTagLength) bytes in the input buffer
//   //    afterwards, so that there is always enough space to read the
//   //    next tag without checking for a refill.
//   //  - TryFastAppend must always return decline (return false)
//   //    if <length> is 61 or more, as in this case the literal length is not
//   //    decoded fully. In practice, this should not be a big problem,
//   //    as it is unlikely that one would implement a fast path accepting
//   //    this much data.
//   //
//   bool TryFastAppend(const char* ip, size_t available, size_t length);
// };

static inline uint32 ExtractLowBytes(uint32 v, int n) {
  assert(n >= 0);
  assert(n <= 4);
#if SNAPPY_HAVE_BMI2
  return _bzhi_u32(v, 8 * n);
#else
  // This needs to be wider than uint32 otherwise `mask << 32` will be
  // undefined.
  uint64 mask = 0xffffffff;
  return v & ~(mask << (8 * n));
#endif
}

static inline bool LeftShiftOverflows(uint8 value, uint32 shift) {
  assert(shift < 32);
  static const uint8 masks[] = {
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  //
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  //
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  //
      0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe};
  return (value & masks[shift]) != 0;
}

// Helper class for decompression
class SnappyDecompressor {
 private:
  Source*       reader_;         // Underlying source of bytes to decompress
  const char*   ip_;             // Points to next buffered byte
  const char*   ip_limit_;       // Points just past buffered bytes
  uint32        peeked_;         // Bytes peeked from reader (need to skip)
  bool          eof_;            // Hit end of input without an error?
  char          scratch_[kMaximumTagLength];  // See RefillTag().

  // Ensure that all of the tag metadata for the next tag is available
  // in [ip_..ip_limit_-1].  Also ensures that [ip,ip+4] is readable even
  // if (ip_limit_ - ip_ < 5).
  //
  // Returns true on success, false on error or end of input.
  bool RefillTag();

 public:
  explicit SnappyDecompressor(Source* reader)
      : reader_(reader),
        ip_(NULL),
        ip_limit_(NULL),
        peeked_(0),
        eof_(false) {
  }

  ~SnappyDecompressor() {
    // Advance past any bytes we peeked at from the reader
    reader_->Skip(peeked_);
  }

  // Returns true iff we have hit the end of the input without an error.
  bool eof() const {
    return eof_;
  }

  // Read the uncompressed length stored at the start of the compressed data.
  // On success, stores the length in *result and returns true.
  // On failure, returns false.
  bool ReadUncompressedLength(uint32* result) {
    assert(ip_ == NULL);       // Must not have read anything yet
    // Length is encoded in 1..5 bytes
    *result = 0;
    uint32 shift = 0;
    while (true) {
      if (shift >= 32) return false;
      size_t n;
      const char* ip = reader_->Peek(&n);
      if (n == 0) return false;
      const unsigned char c = *(reinterpret_cast<const unsigned char*>(ip));
      reader_->Skip(1);
      uint32 val = c & 0x7f;
      if (LeftShiftOverflows(static_cast<uint8>(val), shift)) return false;
      *result |= val << shift;
      if (c < 128) {
        break;
      }
      shift += 7;
    }
    return true;
  }

  // Process the next item found in the input.
  // Returns true if successful, false on error or end of input.
  template <class Writer>
#if defined(__GNUC__) && defined(__x86_64__)
  __attribute__((aligned(32)))
#endif
  void DecompressAllTags(Writer* writer) {
    // In x86, pad the function body to start 16 bytes later. This function has
    // a couple of hotspots that are highly sensitive to alignment: we have
    // observed regressions by more than 20% in some metrics just by moving the
    // exact same code to a different position in the benchmark binary.
    //
    // Putting this code on a 32-byte-aligned boundary + 16 bytes makes us hit
    // the "lucky" case consistently. Unfortunately, this is a very brittle
    // workaround, and future differences in code generation may reintroduce
    // this regression. If you experience a big, difficult to explain, benchmark
    // performance regression here, first try removing this hack.
#if defined(__GNUC__) && defined(__x86_64__)
    // Two 8-byte "NOP DWORD ptr [EAX + EAX*1 + 00000000H]" instructions.
    asm(".byte 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00");
    asm(".byte 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00");
#endif

    const char* ip = ip_;
    // We could have put this refill fragment only at the beginning of the loop.
    // However, duplicating it at the end of each branch gives the compiler more
    // scope to optimize the <ip_limit_ - ip> expression based on the local
    // context, which overall increases speed.
    #define MAYBE_REFILL() \
        if (ip_limit_ - ip < kMaximumTagLength) { \
          ip_ = ip; \
          if (!RefillTag()) return; \
          ip = ip_; \
        }

    MAYBE_REFILL();
    for ( ;; ) {
      const unsigned char c = *(reinterpret_cast<const unsigned char*>(ip++));

      // Ratio of iterations that have LITERAL vs non-LITERAL for different
      // inputs.
      //
      // input          LITERAL  NON_LITERAL
      // -----------------------------------
      // html|html4|cp   23%        77%
      // urls            36%        64%
      // jpg             47%        53%
      // pdf             19%        81%
      // txt[1-4]        25%        75%
      // pb              24%        76%
      // bin             24%        76%
      if (SNAPPY_PREDICT_FALSE((c & 0x3) == LITERAL)) {
        size_t literal_length = (c >> 2) + 1u;
        if (writer->TryFastAppend(ip, ip_limit_ - ip, literal_length)) {
          assert(literal_length < 61);
          ip += literal_length;
          // NOTE: There is no MAYBE_REFILL() here, as TryFastAppend()
          // will not return true unless there's already at least five spare
          // bytes in addition to the literal.
          continue;
        }
        if (SNAPPY_PREDICT_FALSE(literal_length >= 61)) {
          // Long literal.
          const size_t literal_length_length = literal_length - 60;
          literal_length =
              ExtractLowBytes(LittleEndian::Load32(ip), literal_length_length) +
              1;
          ip += literal_length_length;
        }

        size_t avail = ip_limit_ - ip;
        while (avail < literal_length) {
          if (!writer->Append(ip, avail)) return;
          literal_length -= avail;
          reader_->Skip(peeked_);
          size_t n;
          ip = reader_->Peek(&n);
          avail = n;
          peeked_ = avail;
          if (avail == 0) return;  // Premature end of input
          ip_limit_ = ip + avail;
        }
        if (!writer->Append(ip, literal_length)) {
          return;
        }
        ip += literal_length;
        MAYBE_REFILL();
      } else {
        const size_t entry = char_table[c];
        const size_t trailer =
            ExtractLowBytes(LittleEndian::Load32(ip), entry >> 11);
        const size_t length = entry & 0xff;
        ip += entry >> 11;

        // copy_offset/256 is encoded in bits 8..10.  By just fetching
        // those bits, we get copy_offset (since the bit-field starts at
        // bit 8).
        const size_t copy_offset = entry & 0x700;
        if (!writer->AppendFromSelf(copy_offset + trailer, length)) {
          return;
        }
        MAYBE_REFILL();
      }
    }

#undef MAYBE_REFILL
  }
};

bool SnappyDecompressor::RefillTag() {
  const char* ip = ip_;
  if (ip == ip_limit_) {
    // Fetch a new fragment from the reader
    reader_->Skip(peeked_);   // All peeked bytes are used up
    size_t n;
    ip = reader_->Peek(&n);
    peeked_ = n;
    eof_ = (n == 0);
    if (eof_) return false;
    ip_limit_ = ip + n;
  }

  // Read the tag character
  assert(ip < ip_limit_);
  const unsigned char c = *(reinterpret_cast<const unsigned char*>(ip));
  const uint32 entry = char_table[c];
  const uint32 needed = (entry >> 11) + 1;  // +1 byte for 'c'
  assert(needed <= sizeof(scratch_));

  // Read more bytes from reader if needed
  uint32 nbuf = ip_limit_ - ip;
  if (nbuf < needed) {
    // Stitch together bytes from ip and reader to form the word
    // contents.  We store the needed bytes in "scratch_".  They
    // will be consumed immediately by the caller since we do not
    // read more than we need.
    memmove(scratch_, ip, nbuf);
    reader_->Skip(peeked_);  // All peeked bytes are used up
    peeked_ = 0;
    while (nbuf < needed) {
      size_t length;
      const char* src = reader_->Peek(&length);
      if (length == 0) return false;
      uint32 to_add = std::min<uint32>(needed - nbuf, length);
      memcpy(scratch_ + nbuf, src, to_add);
      nbuf += to_add;
      reader_->Skip(to_add);
    }
    assert(nbuf == needed);
    ip_ = scratch_;
    ip_limit_ = scratch_ + needed;
  } else if (nbuf < kMaximumTagLength) {
    // Have enough bytes, but move into scratch_ so that we do not
    // read past end of input
    memmove(scratch_, ip, nbuf);
    reader_->Skip(peeked_);  // All peeked bytes are used up
    peeked_ = 0;
    ip_ = scratch_;
    ip_limit_ = scratch_ + nbuf;
  } else {
    // Pass pointer to buffer returned by reader_.
    ip_ = ip;
  }
  return true;
}

template <typename Writer>
static bool InternalUncompress(Source* r, Writer* writer) {
  // Read the uncompressed length from the front of the compressed input
  SnappyDecompressor decompressor(r);
  uint32 uncompressed_len = 0;
  if (!decompressor.ReadUncompressedLength(&uncompressed_len)) return false;

  return InternalUncompressAllTags(&decompressor, writer, r->Available(),
                                   uncompressed_len);
}

template <typename Writer>
static bool InternalUncompressAllTags(SnappyDecompressor* decompressor,
                                      Writer* writer,
                                      uint32 compressed_len,
                                      uint32 uncompressed_len) {
  Report("snappy_uncompress", compressed_len, uncompressed_len);

  writer->SetExpectedLength(uncompressed_len);

  // Process the entire input
  decompressor->DecompressAllTags(writer);
  writer->Flush();
  return (decompressor->eof() && writer->CheckLength());
}

bool GetUncompressedLength(Source* source, uint32* result) {
  SnappyDecompressor decompressor(source);
  return decompressor.ReadUncompressedLength(result);
}

size_t Compress(Source* reader, Sink* writer) {
  size_t written = 0;
  size_t N = reader->Available();
  const size_t uncompressed_size = N;
  char ulength[Varint::kMax32];
  char* p = Varint::Encode32(ulength, N);
  writer->Append(ulength, p-ulength);
  written += (p - ulength);

  internal::WorkingMemory wmem(N);

  while (N > 0) {
    // Get next block to compress (without copying if possible)
    size_t fragment_size;
    const char* fragment = reader->Peek(&fragment_size);
    assert(fragment_size != 0);  // premature end of input
    const size_t num_to_read = std::min(N, kBlockSize);
    size_t bytes_read = fragment_size;

    size_t pending_advance = 0;
    if (bytes_read >= num_to_read) {
      // Buffer returned by reader is large enough
      pending_advance = num_to_read;
      fragment_size = num_to_read;
    } else {
      char* scratch = wmem.GetScratchInput();
      memcpy(scratch, fragment, bytes_read);
      reader->Skip(bytes_read);

      while (bytes_read < num_to_read) {
        fragment = reader->Peek(&fragment_size);
        size_t n = std::min<size_t>(fragment_size, num_to_read - bytes_read);
        memcpy(scratch + bytes_read, fragment, n);
        bytes_read += n;
        reader->Skip(n);
      }
      assert(bytes_read == num_to_read);
      fragment = scratch;
      fragment_size = num_to_read;
    }
    assert(fragment_size == num_to_read);

    // Get encoding table for compression
    int table_size;
    uint16* table = wmem.GetHashTable(num_to_read, &table_size);

    // Compress input_fragment and append to dest
    const int max_output = MaxCompressedLength(num_to_read);

    // Need a scratch buffer for the output, in case the byte sink doesn't
    // have room for us directly.

    // Since we encode kBlockSize regions followed by a region
    // which is <= kBlockSize in length, a previously allocated
    // scratch_output[] region is big enough for this iteration.
    char* dest = writer->GetAppendBuffer(max_output, wmem.GetScratchOutput());
    char* end = internal::CompressFragment(fragment, fragment_size, dest, table,
                                           table_size);
    writer->Append(dest, end - dest);
    written += (end - dest);

    N -= num_to_read;
    reader->Skip(pending_advance);
  }

  Report("snappy_compress", written, uncompressed_size);

  return written;
}

// -----------------------------------------------------------------------
// IOVec interfaces
// -----------------------------------------------------------------------

// A type that writes to an iovec.
// Note that this is not a "ByteSink", but a type that matches the
// Writer template argument to SnappyDecompressor::DecompressAllTags().
class SnappyIOVecWriter {
 private:
  // output_iov_end_ is set to iov + count and used to determine when
  // the end of the iovs is reached.
  const struct iovec* output_iov_end_;

#if !defined(NDEBUG)
  const struct iovec* output_iov_;
#endif  // !defined(NDEBUG)

  // Current iov that is being written into.
  const struct iovec* curr_iov_;

  // Pointer to current iov's write location.
  char* curr_iov_output_;

  // Remaining bytes to write into curr_iov_output.
  size_t curr_iov_remaining_;

  // Total bytes decompressed into output_iov_ so far.
  size_t total_written_;

  // Maximum number of bytes that will be decompressed into output_iov_.
  size_t output_limit_;

  static inline char* GetIOVecPointer(const struct iovec* iov, size_t offset) {
    return reinterpret_cast<char*>(iov->iov_base) + offset;
  }

 public:
  // Does not take ownership of iov. iov must be valid during the
  // entire lifetime of the SnappyIOVecWriter.
  inline SnappyIOVecWriter(const struct iovec* iov, size_t iov_count)
      : output_iov_end_(iov + iov_count),
#if !defined(NDEBUG)
        output_iov_(iov),
#endif  // !defined(NDEBUG)
        curr_iov_(iov),
        curr_iov_output_(iov_count ? reinterpret_cast<char*>(iov->iov_base)
                                   : nullptr),
        curr_iov_remaining_(iov_count ? iov->iov_len : 0),
        total_written_(0),
        output_limit_(-1) {}

  inline void SetExpectedLength(size_t len) {
    output_limit_ = len;
  }

  inline bool CheckLength() const {
    return total_written_ == output_limit_;
  }

  inline bool Append(const char* ip, size_t len) {
    if (total_written_ + len > output_limit_) {
      return false;
    }

    return AppendNoCheck(ip, len);
  }

  inline bool AppendNoCheck(const char* ip, size_t len) {
    while (len > 0) {
      if (curr_iov_remaining_ == 0) {
        // This iovec is full. Go to the next one.
        if (curr_iov_ + 1 >= output_iov_end_) {
          return false;
        }
        ++curr_iov_;
        curr_iov_output_ = reinterpret_cast<char*>(curr_iov_->iov_base);
        curr_iov_remaining_ = curr_iov_->iov_len;
      }

      const size_t to_write = std::min(len, curr_iov_remaining_);
      memcpy(curr_iov_output_, ip, to_write);
      curr_iov_output_ += to_write;
      curr_iov_remaining_ -= to_write;
      total_written_ += to_write;
      ip += to_write;
      len -= to_write;
    }

    return true;
  }

  inline bool TryFastAppend(const char* ip, size_t available, size_t len) {
    const size_t space_left = output_limit_ - total_written_;
    if (len <= 16 && available >= 16 + kMaximumTagLength && space_left >= 16 &&
        curr_iov_remaining_ >= 16) {
      // Fast path, used for the majority (about 95%) of invocations.
      UnalignedCopy128(ip, curr_iov_output_);
      curr_iov_output_ += len;
      curr_iov_remaining_ -= len;
      total_written_ += len;
      return true;
    }

    return false;
  }

  inline bool AppendFromSelf(size_t offset, size_t len) {
    // See SnappyArrayWriter::AppendFromSelf for an explanation of
    // the "offset - 1u" trick.
    if (offset - 1u >= total_written_) {
      return false;
    }
    const size_t space_left = output_limit_ - total_written_;
    if (len > space_left) {
      return false;
    }

    // Locate the iovec from which we need to start the copy.
    const iovec* from_iov = curr_iov_;
    size_t from_iov_offset = curr_iov_->iov_len - curr_iov_remaining_;
    while (offset > 0) {
      if (from_iov_offset >= offset) {
        from_iov_offset -= offset;
        break;
      }

      offset -= from_iov_offset;
      --from_iov;
#if !defined(NDEBUG)
      assert(from_iov >= output_iov_);
#endif  // !defined(NDEBUG)
      from_iov_offset = from_iov->iov_len;
    }

    // Copy <len> bytes starting from the iovec pointed to by from_iov_index to
    // the current iovec.
    while (len > 0) {
      assert(from_iov <= curr_iov_);
      if (from_iov != curr_iov_) {
        const size_t to_copy =
            std::min((unsigned long)(from_iov->iov_len - from_iov_offset), (unsigned long)len);
        AppendNoCheck(GetIOVecPointer(from_iov, from_iov_offset), to_copy);
        len -= to_copy;
        if (len > 0) {
          ++from_iov;
          from_iov_offset = 0;
        }
      } else {
        size_t to_copy = curr_iov_remaining_;
        if (to_copy == 0) {
          // This iovec is full. Go to the next one.
          if (curr_iov_ + 1 >= output_iov_end_) {
            return false;
          }
          ++curr_iov_;
          curr_iov_output_ = reinterpret_cast<char*>(curr_iov_->iov_base);
          curr_iov_remaining_ = curr_iov_->iov_len;
          continue;
        }
        if (to_copy > len) {
          to_copy = len;
        }

        IncrementalCopy(GetIOVecPointer(from_iov, from_iov_offset),
                        curr_iov_output_, curr_iov_output_ + to_copy,
                        curr_iov_output_ + curr_iov_remaining_);
        curr_iov_output_ += to_copy;
        curr_iov_remaining_ -= to_copy;
        from_iov_offset += to_copy;
        total_written_ += to_copy;
        len -= to_copy;
      }
    }

    return true;
  }

  inline void Flush() {}
};

bool RawUncompressToIOVec(const char* compressed, size_t compressed_length,
                          const struct iovec* iov, size_t iov_cnt) {
  ByteArraySource reader(compressed, compressed_length);
  return RawUncompressToIOVec(&reader, iov, iov_cnt);
}

bool RawUncompressToIOVec(Source* compressed, const struct iovec* iov,
                          size_t iov_cnt) {
  SnappyIOVecWriter output(iov, iov_cnt);
  return InternalUncompress(compressed, &output);
}

// -----------------------------------------------------------------------
// Flat array interfaces
// -----------------------------------------------------------------------

// A type that writes to a flat array.
// Note that this is not a "ByteSink", but a type that matches the
// Writer template argument to SnappyDecompressor::DecompressAllTags().
class SnappyArrayWriter {
 private:
  char* base_;
  char* op_;
  char* op_limit_;

 public:
  inline explicit SnappyArrayWriter(char* dst)
      : base_(dst),
        op_(dst),
        op_limit_(dst) {
  }

  inline void SetExpectedLength(size_t len) {
    op_limit_ = op_ + len;
  }

  inline bool CheckLength() const {
    return op_ == op_limit_;
  }

  inline bool Append(const char* ip, size_t len) {
    char* op = op_;
    const size_t space_left = op_limit_ - op;
    if (space_left < len) {
      return false;
    }
    memcpy(op, ip, len);
    op_ = op + len;
    return true;
  }

  inline bool TryFastAppend(const char* ip, size_t available, size_t len) {
    char* op = op_;
    const size_t space_left = op_limit_ - op;
    if (len <= 16 && available >= 16 + kMaximumTagLength && space_left >= 16) {
      // Fast path, used for the majority (about 95%) of invocations.
      UnalignedCopy128(ip, op);
      op_ = op + len;
      return true;
    } else {
      return false;
    }
  }

  inline bool AppendFromSelf(size_t offset, size_t len) {
    char* const op_end = op_ + len;

    // Check if we try to append from before the start of the buffer.
    // Normally this would just be a check for "produced < offset",
    // but "produced <= offset - 1u" is equivalent for every case
    // except the one where offset==0, where the right side will wrap around
    // to a very big number. This is convenient, as offset==0 is another
    // invalid case that we also want to catch, so that we do not go
    // into an infinite loop.
    if (Produced() <= offset - 1u || op_end > op_limit_) return false;
    op_ = IncrementalCopy(op_ - offset, op_, op_end, op_limit_);

    return true;
  }
  inline size_t Produced() const {
    assert(op_ >= base_);
    return op_ - base_;
  }
  inline void Flush() {}
};

bool RawUncompress(const char* compressed, size_t n, char* uncompressed) {
  ByteArraySource reader(compressed, n);
  return RawUncompress(&reader, uncompressed);
}

bool RawUncompress(Source* compressed, char* uncompressed) {
  SnappyArrayWriter output(uncompressed);
  return InternalUncompress(compressed, &output);
}

bool Uncompress(const char* compressed, size_t n, string* uncompressed) {
  size_t ulength;
  if (!GetUncompressedLength(compressed, n, &ulength)) {
    return false;
  }
  // On 32-bit builds: max_size() < kuint32max.  Check for that instead
  // of crashing (e.g., consider externally specified compressed data).
  if (ulength > uncompressed->max_size()) {
    return false;
  }
  STLStringResizeUninitialized(uncompressed, ulength);
  return RawUncompress(compressed, n, string_as_array(uncompressed));
}

// A Writer that drops everything on the floor and just does validation
class SnappyDecompressionValidator {
 private:
  size_t expected_;
  size_t produced_;

 public:
  inline SnappyDecompressionValidator() : expected_(0), produced_(0) { }
  inline void SetExpectedLength(size_t len) {
    expected_ = len;
  }
  inline bool CheckLength() const {
    return expected_ == produced_;
  }
  inline bool Append(const char* ip, size_t len) {
    produced_ += len;
    return produced_ <= expected_;
  }
  inline bool TryFastAppend(const char* ip, size_t available, size_t length) {
    return false;
  }
  inline bool AppendFromSelf(size_t offset, size_t len) {
    // See SnappyArrayWriter::AppendFromSelf for an explanation of
    // the "offset - 1u" trick.
    if (produced_ <= offset - 1u) return false;
    produced_ += len;
    return produced_ <= expected_;
  }
  inline void Flush() {}
};

bool IsValidCompressedBuffer(const char* compressed, size_t n) {
  ByteArraySource reader(compressed, n);
  SnappyDecompressionValidator writer;
  return InternalUncompress(&reader, &writer);
}

bool IsValidCompressed(Source* compressed) {
  SnappyDecompressionValidator writer;
  return InternalUncompress(compressed, &writer);
}

void RawCompress(const char* input,
                 size_t input_length,
                 char* compressed,
                 size_t* compressed_length) {
  ByteArraySource reader(input, input_length);
  UncheckedByteArraySink writer(compressed);
  Compress(&reader, &writer);

  // Compute how many bytes were added
  *compressed_length = (writer.CurrentDestination() - compressed);
}

size_t Compress(const char* input, size_t input_length, string* compressed) {
  // Pre-grow the buffer to the max length of the compressed output
  STLStringResizeUninitialized(compressed, MaxCompressedLength(input_length));

  size_t compressed_length;
  RawCompress(input, input_length, string_as_array(compressed),
              &compressed_length);
  compressed->resize(compressed_length);
  return compressed_length;
}

// -----------------------------------------------------------------------
// Sink interface
// -----------------------------------------------------------------------

// A type that decompresses into a Sink. The template parameter
// Allocator must export one method "char* Allocate(int size);", which
// allocates a buffer of "size" and appends that to the destination.
template <typename Allocator>
class SnappyScatteredWriter {
  Allocator allocator_;

  // We need random access into the data generated so far.  Therefore
  // we keep track of all of the generated data as an array of blocks.
  // All of the blocks except the last have length kBlockSize.
  std::vector<char*> blocks_;
  size_t expected_;

  // Total size of all fully generated blocks so far
  size_t full_size_;

  // Pointer into current output block
  char* op_base_;       // Base of output block
  char* op_ptr_;        // Pointer to next unfilled byte in block
  char* op_limit_;      // Pointer just past block

  inline size_t Size() const {
    return full_size_ + (op_ptr_ - op_base_);
  }

  bool SlowAppend(const char* ip, size_t len);
  bool SlowAppendFromSelf(size_t offset, size_t len);

 public:
  inline explicit SnappyScatteredWriter(const Allocator& allocator)
      : allocator_(allocator),
        full_size_(0),
        op_base_(NULL),
        op_ptr_(NULL),
        op_limit_(NULL) {
  }

  inline void SetExpectedLength(size_t len) {
    assert(blocks_.empty());
    expected_ = len;
  }

  inline bool CheckLength() const {
    return Size() == expected_;
  }

  // Return the number of bytes actually uncompressed so far
  inline size_t Produced() const {
    return Size();
  }

  inline bool Append(const char* ip, size_t len) {
    size_t avail = op_limit_ - op_ptr_;
    if (len <= avail) {
      // Fast path
      memcpy(op_ptr_, ip, len);
      op_ptr_ += len;
      return true;
    } else {
      return SlowAppend(ip, len);
    }
  }

  inline bool TryFastAppend(const char* ip, size_t available, size_t length) {
    char* op = op_ptr_;
    const int space_left = op_limit_ - op;
    if (length <= 16 && available >= 16 + kMaximumTagLength &&
        space_left >= 16) {
      // Fast path, used for the majority (about 95%) of invocations.
      UnalignedCopy128(ip, op);
      op_ptr_ = op + length;
      return true;
    } else {
      return false;
    }
  }

  inline bool AppendFromSelf(size_t offset, size_t len) {
    char* const op_end = op_ptr_ + len;
    // See SnappyArrayWriter::AppendFromSelf for an explanation of
    // the "offset - 1u" trick.
    if (SNAPPY_PREDICT_TRUE(offset - 1u < (size_t)(op_ptr_ - op_base_) &&
                          op_end <= op_limit_)) {
      // Fast path: src and dst in current block.
      op_ptr_ = IncrementalCopy(op_ptr_ - offset, op_ptr_, op_end, op_limit_);
      return true;
    }
    return SlowAppendFromSelf(offset, len);
  }

  // Called at the end of the decompress. We ask the allocator
  // write all blocks to the sink.
  inline void Flush() { allocator_.Flush(Produced()); }
};

template<typename Allocator>
bool SnappyScatteredWriter<Allocator>::SlowAppend(const char* ip, size_t len) {
  size_t avail = op_limit_ - op_ptr_;
  while (len > avail) {
    // Completely fill this block
    memcpy(op_ptr_, ip, avail);
    op_ptr_ += avail;
    assert(op_limit_ - op_ptr_ == 0);
    full_size_ += (op_ptr_ - op_base_);
    len -= avail;
    ip += avail;

    // Bounds check
    if (full_size_ + len > expected_) {
      return false;
    }

    // Make new block
    size_t bsize = std::min<size_t>(kBlockSize, expected_ - full_size_);
    op_base_ = allocator_.Allocate(bsize);
    op_ptr_ = op_base_;
    op_limit_ = op_base_ + bsize;
    blocks_.push_back(op_base_);
    avail = bsize;
  }

  memcpy(op_ptr_, ip, len);
  op_ptr_ += len;
  return true;
}

template<typename Allocator>
bool SnappyScatteredWriter<Allocator>::SlowAppendFromSelf(size_t offset,
                                                         size_t len) {
  // Overflow check
  // See SnappyArrayWriter::AppendFromSelf for an explanation of
  // the "offset - 1u" trick.
  const size_t cur = Size();
  if (offset - 1u >= cur) return false;
  if (expected_ - cur < len) return false;

  // Currently we shouldn't ever hit this path because Compress() chops the
  // input into blocks and does not create cross-block copies. However, it is
  // nice if we do not rely on that, since we can get better compression if we
  // allow cross-block copies and thus might want to change the compressor in
  // the future.
  size_t src = cur - offset;
  while (len-- > 0) {
    char c = blocks_[src >> kBlockLog][src & (kBlockSize-1)];
    Append(&c, 1);
    src++;
  }
  return true;
}

class SnappySinkAllocator {
 public:
  explicit SnappySinkAllocator(Sink* dest): dest_(dest) {}
  ~SnappySinkAllocator() {}

  char* Allocate(int size) {
    Datablock block(new char[size], size);
    blocks_.push_back(block);
    return block.data;
  }

  // We flush only at the end, because the writer wants
  // random access to the blocks and once we hand the
  // block over to the sink, we can't access it anymore.
  // Also we don't write more than has been actually written
  // to the blocks.
  void Flush(size_t size) {
    size_t size_written = 0;
    size_t block_size;
    for (size_t i = 0; i < blocks_.size(); ++i) {
      block_size = std::min<size_t>(blocks_[i].size, size - size_written);
      dest_->AppendAndTakeOwnership(blocks_[i].data, block_size,
                                    &SnappySinkAllocator::Deleter, NULL);
      size_written += block_size;
    }
    blocks_.clear();
  }

 private:
  struct Datablock {
    char* data;
    size_t size;
    Datablock(char* p, size_t s) : data(p), size(s) {}
  };

  static void Deleter(void* arg, const char* bytes, size_t size) {
    delete[] bytes;
  }

  Sink* dest_;
  std::vector<Datablock> blocks_;

  // Note: copying this object is allowed
};

size_t UncompressAsMuchAsPossible(Source* compressed, Sink* uncompressed) {
  SnappySinkAllocator allocator(uncompressed);
  SnappyScatteredWriter<SnappySinkAllocator> writer(allocator);
  InternalUncompress(compressed, &writer);
  return writer.Produced();
}

bool Uncompress(Source* compressed, Sink* uncompressed) {
  // Read the uncompressed length from the front of the compressed input
  SnappyDecompressor decompressor(compressed);
  uint32 uncompressed_len = 0;
  if (!decompressor.ReadUncompressedLength(&uncompressed_len)) {
    return false;
  }

  char c;
  size_t allocated_size;
  char* buf = uncompressed->GetAppendBufferVariable(
      1, uncompressed_len, &c, 1, &allocated_size);

  const size_t compressed_len = compressed->Available();
  // If we can get a flat buffer, then use it, otherwise do block by block
  // uncompression
  if (allocated_size >= uncompressed_len) {
    SnappyArrayWriter writer(buf);
    bool result = InternalUncompressAllTags(&decompressor, &writer,
                                            compressed_len, uncompressed_len);
    uncompressed->Append(buf, writer.Produced());
    return result;
  } else {
    SnappySinkAllocator allocator(uncompressed);
    SnappyScatteredWriter<SnappySinkAllocator> writer(allocator);
    return InternalUncompressAllTags(&decompressor, &writer, compressed_len,
                                     uncompressed_len);
  }
}

}  // namespace snappy

