// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc.  All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd

// Author: kenton@google.com (Kenton Varda)
//  Based on original Protocol Buffers design by
//  Sanjay Ghemawat, Jeff Dean, and others.
//
// This file contains the ZeroCopyInputStream and ZeroCopyOutputStream
// interfaces, which represent abstract I/O streams to and from which
// protocol buffers can be read and written.  For a few simple
// implementations of these interfaces, see zero_copy_stream_impl.h.
//
// These interfaces are different from classic I/O streams in that they
// try to minimize the amount of data copying that needs to be done.
// To accomplish this, responsibility for allocating buffers is moved to
// the stream object, rather than being the responsibility of the caller.
// So, the stream can return a buffer which actually points directly into
// the final data structure where the bytes are to be stored, and the caller
// can interact directly with that buffer, eliminating an intermediate copy
// operation.
//
// As an example, consider the common case in which you are reading bytes
// from an array that is already in memory (or perhaps an mmap()ed file).
// With classic I/O streams, you would do something like:
//   char buffer[BUFFER_SIZE];
//   input->Read(buffer, BUFFER_SIZE);
//   DoSomething(buffer, BUFFER_SIZE);
// Then, the stream basically just calls memcpy() to copy the data from
// the array into your buffer.  With a ZeroCopyInputStream, you would do
// this instead:
//   const void* buffer;
//   int size;
//   input->Next(&buffer, &size);
//   DoSomething(buffer, size);
// Here, no copy is performed.  The input stream returns a pointer directly
// into the backing array, and the caller ends up reading directly from it.
//
// If you want to be able to read the old-fashion way, you can create
// a CodedInputStream or CodedOutputStream wrapping these objects and use
// their ReadRaw()/WriteRaw() methods.  These will, of course, add a copy
// step, but Coded*Stream will handle buffering so at least it will be
// reasonably efficient.
//
// ZeroCopyInputStream example:
//   // Read in a file and print its contents to stdout.
//   int fd = open("myfile", O_RDONLY);
//   ZeroCopyInputStream* input = new FileInputStream(fd);
//
//   const void* buffer;
//   int size;
//   while (input->Next(&buffer, &size)) {
//     cout.write(buffer, size);
//   }
//
//   delete input;
//   close(fd);
//
// ZeroCopyOutputStream example:
//   // Copy the contents of "infile" to "outfile", using plain read() for
//   // "infile" but a ZeroCopyOutputStream for "outfile".
//   int infd = open("infile", O_RDONLY);
//   int outfd = open("outfile", O_WRONLY);
//   ZeroCopyOutputStream* output = new FileOutputStream(outfd);
//
//   void* buffer;
//   int size;
//   while (output->Next(&buffer, &size)) {
//     int bytes = read(infd, buffer, size);
//     if (bytes < size) {
//       // Reached EOF.
//       output->BackUp(size - bytes);
//       break;
//     }
//   }
//
//   delete output;
//   close(infd);
//   close(outfd);

#ifndef GOOGLE_PROTOBUF_IO_ZERO_COPY_STREAM_H__
#define GOOGLE_PROTOBUF_IO_ZERO_COPY_STREAM_H__

#include <cstdint>

#include "absl/strings/cord.h"


// Must be included last.
#include "google/protobuf/port_def.inc"

namespace google {
namespace protobuf {
namespace io {

// Abstract interface similar to an input stream but designed to minimize
// copying.
class PROTOBUF_EXPORT PROTOBUF_FUTURE_ADD_EARLY_WARN_UNUSED
    ZeroCopyInputStream {
 public:
  ZeroCopyInputStream() = default;
  virtual ~ZeroCopyInputStream() = default;

  ZeroCopyInputStream(const ZeroCopyInputStream&) = delete;
  ZeroCopyInputStream& operator=(const ZeroCopyInputStream&) = delete;
  ZeroCopyInputStream(ZeroCopyInputStream&&) = delete;
  ZeroCopyInputStream& operator=(ZeroCopyInputStream&&) = delete;

  // Obtains a chunk of data from the stream.
  //
  // Preconditions:
  // * "size" and "data" are not NULL.
  //
  // Postconditions:
  // * If the returned value is false, there is no more data to return or
  //   an error occurred.  All errors are permanent.
  // * Otherwise, "size" points to the actual number of bytes read and "data"
  //   points to a pointer to a buffer containing these bytes.
  // * Ownership of this buffer remains with the stream, and the buffer
  //   remains valid only until some other method of the stream is called
  //   or the stream is destroyed.
  // * It is legal for the returned buffer to have zero size, as long
  //   as repeatedly calling Next() eventually yields a buffer with non-zero
  //   size.
  PROTOBUF_FUTURE_ADD_EARLY_NODISCARD virtual bool Next(const void** data,
                                                        int* size) = 0;

  // Backs up a number of bytes, so that the next call to Next() returns
  // data again that was already returned by the last call to Next().  This
  // is useful when writing procedures that are only supposed to read up
  // to a certain point in the input, then return.  If Next() returns a
  // buffer that goes beyond what you wanted to read, you can use BackUp()
  // to return to the point where you intended to finish.
  //
  // Preconditions:
  // * The last method called must have been Next().
  // * count must be less than or equal to the size of the last buffer
  //   returned by Next().
  //
  // Postconditions:
  // * The last "count" bytes of the last buffer returned by Next() will be
  //   pushed back into the stream.  Subsequent calls to Next() will return
  //   the same data again before producing new data.
  virtual void BackUp(int count) = 0;

  // Skips `count` number of bytes.
  // Returns true on success, or false if some input error occurred, or `count`
  // exceeds the end of the stream. This function may skip up to `count - 1`
  // bytes in case of failure.
  //
  // Preconditions:
  // * `count` is non-negative.
  //
  PROTOBUF_FUTURE_ADD_EARLY_NODISCARD virtual bool Skip(int count) = 0;

  // Returns the total number of bytes read since this object was created.
  PROTOBUF_FUTURE_ADD_EARLY_NODISCARD virtual int64_t ByteCount() const = 0;

  // Read the next `count` bytes and append it to the given Cord.
  //
  // In the case of a read error, the method reads as much data as possible into
  // the cord before returning false. The default implementation iterates over
  // the buffers and appends up to `count` bytes of data into `cord` using the
  // `absl::CordBuffer` API.
  //
  // Some streams may implement this in a way that avoids copying by sharing or
  // reference counting existing data managed by the stream implementation.
  //
  PROTOBUF_FUTURE_ADD_EARLY_NODISCARD virtual bool ReadCord(absl::Cord* cord,
                                                            int count);

};

// Abstract interface similar to an output stream but designed to minimize
// copying.
class PROTOBUF_EXPORT PROTOBUF_FUTURE_ADD_EARLY_WARN_UNUSED
    ZeroCopyOutputStream {
 public:
  ZeroCopyOutputStream() = default;
  ZeroCopyOutputStream(const ZeroCopyOutputStream&) = delete;
  ZeroCopyOutputStream& operator=(const ZeroCopyOutputStream&) = delete;
  virtual ~ZeroCopyOutputStream() = default;

  // Obtains a buffer into which data can be written.  Any data written
  // into this buffer will eventually (maybe instantly, maybe later on)
  // be written to the output.
  //
  // Preconditions:
  // * "size" and "data" are not NULL.
  //
  // Postconditions:
  // * If the returned value is false, an error occurred.  All errors are
  //   permanent.
  // * Otherwise, "size" points to the actual number of bytes in the buffer
  //   and "data" points to the buffer.
  // * Ownership of this buffer remains with the stream, and the buffer
  //   remains valid only until some other method of the stream is called
  //   or the stream is destroyed.
  // * Any data which the caller stores in this buffer will eventually be
  //   written to the output (unless BackUp() is called).
  // * It is legal for the returned buffer to have zero size, as long
  //   as repeatedly calling Next() eventually yields a buffer with non-zero
  //   size.
  PROTOBUF_FUTURE_ADD_EARLY_NODISCARD virtual bool Next(void** data,
                                                        int* size) = 0;

  // Backs up a number of bytes, so that the end of the last buffer returned
  // by Next() is not actually written.  This is needed when you finish
  // writing all the data you want to write, but the last buffer was bigger
  // than you needed.  You don't want to write a bunch of garbage after the
  // end of your data, so you use BackUp() to back up.
  //
  // This method can be called with `count = 0` to finalize (flush) any
  // previously returned buffer. For example, a file output stream can
  // flush buffers returned from a previous call to Next() upon such
  // BackUp(0) invocations. ZeroCopyOutputStream callers should always
  // invoke BackUp() after a final Next() call, even if there is no
  // excess buffer data to be backed up to indicate a flush point.
  //
  // Preconditions:
  // * The last method called must have been Next().
  // * count must be less than or equal to the size of the last buffer
  //   returned by Next().
  // * The caller must not have written anything to the last "count" bytes
  //   of that buffer.
  //
  // Postconditions:
  // * The last "count" bytes of the last buffer returned by Next() will be
  //   ignored.
  virtual void BackUp(int count) = 0;

  // Returns the total number of bytes written since this object was created.
  PROTOBUF_FUTURE_ADD_EARLY_NODISCARD virtual int64_t ByteCount() const = 0;

  // Write a given chunk of data to the output.  Some output streams may
  // implement this in a way that avoids copying. Check AllowsAliasing() before
  // calling WriteAliasedRaw(). It will ABSL_CHECK fail if WriteAliasedRaw() is
  // called on a stream that does not allow aliasing.
  //
  // NOTE: It is caller's responsibility to ensure that the chunk of memory
  // remains live until all of the data has been consumed from the stream.
  PROTOBUF_FUTURE_ADD_EARLY_NODISCARD virtual bool WriteAliasedRaw(
      const void* data, int size);
  PROTOBUF_FUTURE_ADD_EARLY_NODISCARD virtual bool AllowsAliasing() const {
    return false;
  }

  // Writes the given Cord to the output.
  //
  // The default implementation iterates over all Cord chunks copying all cord
  // data into the buffer(s) returned by the stream's `Next()` method.
  //
  // Some streams may implement this in a way that avoids copying the cord
  // data by copying and managing a copy of the provided cord instead.
  PROTOBUF_FUTURE_ADD_EARLY_NODISCARD virtual bool WriteCord(
      const absl::Cord& cord);

};

}  // namespace io
}  // namespace protobuf
}  // namespace google

#include "google/protobuf/port_undef.inc"

#endif  // GOOGLE_PROTOBUF_IO_ZERO_COPY_STREAM_H__
