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

#include "google/protobuf/io/zero_copy_stream_impl_lite.h"

#include <algorithm>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <limits>
#include <string>
#include <utility>

#include "absl/base/casts.h"
#include "absl/log/absl_check.h"
#include "absl/strings/cord.h"
#include "absl/strings/cord_buffer.h"
#include "absl/strings/internal/resize_uninitialized.h"
#include "absl/strings/string_view.h"
#include "absl/types/span.h"
#include "google/protobuf/io/zero_copy_stream.h"


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

namespace google {
namespace protobuf {
namespace io {

namespace {

// Default block size for Copying{In,Out}putStreamAdaptor.
static const int kDefaultBlockSize = 8192;

}  // namespace

// ===================================================================

ArrayInputStream::ArrayInputStream(const void* data, int size, int block_size)
    : data_(reinterpret_cast<const uint8_t*>(data)),
      size_(size),
      block_size_(block_size > 0 ? block_size : size),
      position_(0),
      last_returned_size_(0) {}

bool ArrayInputStream::Next(const void** data, int* size) {
  if (position_ < size_) {
    last_returned_size_ = std::min(block_size_, size_ - position_);
    *data = data_ + position_;
    *size = last_returned_size_;
    position_ += last_returned_size_;
    return true;
  } else {
    // We're at the end of the array.
    last_returned_size_ = 0;  // Don't let caller back up.
    return false;
  }
}

void ArrayInputStream::BackUp(int count) {
  ABSL_CHECK_GT(last_returned_size_, 0)
      << "BackUp() can only be called after a successful Next().";
  ABSL_CHECK_LE(count, last_returned_size_);
  ABSL_CHECK_GE(count, 0);
  position_ -= count;
  last_returned_size_ = 0;  // Don't let caller back up further.
}

bool ArrayInputStream::Skip(int count) {
  ABSL_CHECK_GE(count, 0);
  last_returned_size_ = 0;  // Don't let caller back up.
  if (count > size_ - position_) {
    position_ = size_;
    return false;
  } else {
    position_ += count;
    return true;
  }
}

int64_t ArrayInputStream::ByteCount() const { return position_; }


// ===================================================================

ArrayOutputStream::ArrayOutputStream(void* data, int size, int block_size)
    : data_(reinterpret_cast<uint8_t*>(data)),
      size_(size),
      block_size_(block_size > 0 ? block_size : size),
      position_(0),
      last_returned_size_(0) {}

bool ArrayOutputStream::Next(void** data, int* size) {
  if (position_ < size_) {
    last_returned_size_ = std::min(block_size_, size_ - position_);
    *data = data_ + position_;
    *size = last_returned_size_;
    position_ += last_returned_size_;
    return true;
  } else {
    // We're at the end of the array.
    last_returned_size_ = 0;  // Don't let caller back up.
    return false;
  }
}

void ArrayOutputStream::BackUp(int count) {
  ABSL_CHECK_LE(count, last_returned_size_)
      << "BackUp() can not exceed the size of the last Next() call.";
  ABSL_CHECK_GE(count, 0);
  position_ -= count;
  last_returned_size_ -= count;
}

int64_t ArrayOutputStream::ByteCount() const { return position_; }

// ===================================================================

StringOutputStream::StringOutputStream(std::string* target) : target_(target) {}

bool StringOutputStream::Next(void** data, int* size) {
  ABSL_CHECK(target_ != nullptr);
  size_t old_size = target_->size();

  // Grow the string.
  size_t new_size;
  if (old_size < target_->capacity()) {
    // Resize the string to match its capacity, since we can get away
    // without a memory allocation this way.
    new_size = target_->capacity();
  } else {
    // Size has reached capacity, try to double it.
    new_size = old_size * 2;
  }
  // Avoid integer overflow in returned '*size'.
  new_size = std::min(new_size, old_size + std::numeric_limits<int>::max());
  // Increase the size, also make sure that it is at least kMinimumSize.
  absl::strings_internal::STLStringResizeUninitialized(
      target_,
      std::max(new_size,
               kMinimumSize + 0));  // "+ 0" works around GCC4 weirdness.

  *data = mutable_string_data(target_) + old_size;
  *size = target_->size() - old_size;
  return true;
}

void StringOutputStream::BackUp(int count) {
  ABSL_CHECK_GE(count, 0);
  ABSL_CHECK(target_ != nullptr);
  ABSL_CHECK_LE(static_cast<size_t>(count), target_->size());
  target_->resize(target_->size() - count);
}

int64_t StringOutputStream::ByteCount() const {
  ABSL_CHECK(target_ != nullptr);
  return target_->size();
}

// ===================================================================

int CopyingInputStream::Skip(int count) {
  char junk[4096];
  int skipped = 0;
  while (skipped < count) {
    int bytes = Read(junk, std::min(count - skipped,
                                    absl::implicit_cast<int>(sizeof(junk))));
    if (bytes <= 0) {
      // EOF or read error.
      return skipped;
    }
    skipped += bytes;
  }
  return skipped;
}

CopyingInputStreamAdaptor::CopyingInputStreamAdaptor(
    CopyingInputStream* copying_stream, int block_size)
    : copying_stream_(copying_stream),
      owns_copying_stream_(false),
      failed_(false),
      position_(0),
      buffer_size_(block_size > 0 ? block_size : kDefaultBlockSize),
      buffer_used_(0),
      backup_bytes_(0) {}

CopyingInputStreamAdaptor::~CopyingInputStreamAdaptor() {
  if (owns_copying_stream_) {
    delete copying_stream_;
  }
}

bool CopyingInputStreamAdaptor::Next(const void** data, int* size) {
  if (failed_) {
    // Already failed on a previous read.
    return false;
  }

  AllocateBufferIfNeeded();

  if (backup_bytes_ > 0) {
    // We have data left over from a previous BackUp(), so just return that.
    *data = buffer_.get() + buffer_used_ - backup_bytes_;
    *size = backup_bytes_;
    backup_bytes_ = 0;
    return true;
  }

  // Read new data into the buffer.
  buffer_used_ = copying_stream_->Read(buffer_.get(), buffer_size_);
  if (buffer_used_ <= 0) {
    // EOF or read error.  We don't need the buffer anymore.
    if (buffer_used_ < 0) {
      // Read error (not EOF).
      failed_ = true;
    }
    FreeBuffer();
    return false;
  }
  position_ += buffer_used_;

  *size = buffer_used_;
  *data = buffer_.get();
  return true;
}

void CopyingInputStreamAdaptor::BackUp(int count) {
  ABSL_CHECK(backup_bytes_ == 0 && buffer_ != nullptr)
      << " BackUp() can only be called after Next().";
  ABSL_CHECK_LE(count, buffer_used_)
      << " Can't back up over more bytes than were returned by the last call"
         " to Next().";
  ABSL_CHECK_GE(count, 0) << " Parameter to BackUp() can't be negative.";

  backup_bytes_ = count;
}

bool CopyingInputStreamAdaptor::Skip(int count) {
  ABSL_CHECK_GE(count, 0);

  if (failed_) {
    // Already failed on a previous read.
    return false;
  }

  // First skip any bytes left over from a previous BackUp().
  if (backup_bytes_ >= count) {
    // We have more data left over than we're trying to skip.  Just chop it.
    backup_bytes_ -= count;
    return true;
  }

  count -= backup_bytes_;
  backup_bytes_ = 0;

  int skipped = copying_stream_->Skip(count);
  position_ += skipped;
  return skipped == count;
}

int64_t CopyingInputStreamAdaptor::ByteCount() const {
  return position_ - backup_bytes_;
}

void CopyingInputStreamAdaptor::AllocateBufferIfNeeded() {
  if (buffer_ == nullptr) {
    buffer_.reset(new uint8_t[buffer_size_]);
  }
}

void CopyingInputStreamAdaptor::FreeBuffer() {
  ABSL_CHECK_EQ(backup_bytes_, 0);
  buffer_used_ = 0;
  buffer_.reset();
}

// ===================================================================

CopyingOutputStreamAdaptor::CopyingOutputStreamAdaptor(
    CopyingOutputStream* copying_stream, int block_size)
    : copying_stream_(copying_stream),
      owns_copying_stream_(false),
      failed_(false),
      position_(0),
      buffer_size_(block_size > 0 ? block_size : kDefaultBlockSize),
      buffer_used_(0) {}

CopyingOutputStreamAdaptor::~CopyingOutputStreamAdaptor() {
  WriteBuffer();
  if (owns_copying_stream_) {
    delete copying_stream_;
  }
}

bool CopyingOutputStreamAdaptor::Flush() { return WriteBuffer(); }

bool CopyingOutputStreamAdaptor::Next(void** data, int* size) {
  if (buffer_used_ == buffer_size_) {
    if (!WriteBuffer()) return false;
  }

  AllocateBufferIfNeeded();

  *data = buffer_.get() + buffer_used_;
  *size = buffer_size_ - buffer_used_;
  buffer_used_ = buffer_size_;
  return true;
}

void CopyingOutputStreamAdaptor::BackUp(int count) {
  if (count == 0) {
    Flush();
    return;
  }
  ABSL_CHECK_GE(count, 0);
  ABSL_CHECK_EQ(buffer_used_, buffer_size_)
      << " BackUp() can only be called after Next().";
  ABSL_CHECK_LE(count, buffer_used_)
      << " Can't back up over more bytes than were returned by the last call"
         " to Next().";

  buffer_used_ -= count;
}

int64_t CopyingOutputStreamAdaptor::ByteCount() const {
  return position_ + buffer_used_;
}

bool CopyingOutputStreamAdaptor::WriteAliasedRaw(const void* data, int size) {
  if (size >= buffer_size_) {
    if (!Flush() || !copying_stream_->Write(data, size)) {
      return false;
    }
    ABSL_DCHECK_EQ(buffer_used_, 0);
    position_ += size;
    return true;
  }

  void* out;
  int out_size;
  while (true) {
    if (!Next(&out, &out_size)) {
      return false;
    }

    if (size <= out_size) {
      std::memcpy(out, data, size);
      BackUp(out_size - size);
      return true;
    }

    std::memcpy(out, data, out_size);
    data = static_cast<const char*>(data) + out_size;
    size -= out_size;
  }
  return true;
}

bool CopyingOutputStreamAdaptor::WriteCord(const absl::Cord& cord) {
  for (absl::string_view chunk : cord.Chunks()) {
    if (!WriteAliasedRaw(chunk.data(), chunk.size())) {
      return false;
    }
  }
  return true;
}

bool CopyingOutputStreamAdaptor::WriteBuffer() {
  if (failed_) {
    // Already failed on a previous write.
    return false;
  }

  if (buffer_used_ == 0) return true;

  if (copying_stream_->Write(buffer_.get(), buffer_used_)) {
    position_ += buffer_used_;
    buffer_used_ = 0;
    return true;
  } else {
    failed_ = true;
    FreeBuffer();
    return false;
  }
}

void CopyingOutputStreamAdaptor::AllocateBufferIfNeeded() {
  if (buffer_ == nullptr) {
    buffer_.reset(new uint8_t[buffer_size_]);
  }
}

void CopyingOutputStreamAdaptor::FreeBuffer() {
  buffer_used_ = 0;
  buffer_.reset();
}

// ===================================================================

LimitingInputStream::LimitingInputStream(ZeroCopyInputStream* input,
                                         int64_t limit)
    : input_(input), limit_(limit) {
  prior_bytes_read_ = input_->ByteCount();
}

LimitingInputStream::~LimitingInputStream() {
  // If we overshot the limit, back up.
  if (limit_ < 0) input_->BackUp(-limit_);
}

bool LimitingInputStream::Next(const void** data, int* size) {
  if (limit_ <= 0) return false;
  if (!input_->Next(data, size)) return false;

  limit_ -= *size;
  if (limit_ < 0) {
    // We overshot the limit.  Reduce *size to hide the rest of the buffer.
    *size += limit_;
  }
  return true;
}

void LimitingInputStream::BackUp(int count) {
  if (limit_ < 0) {
    input_->BackUp(count - limit_);
    limit_ = count;
  } else {
    input_->BackUp(count);
    limit_ += count;
  }
}

bool LimitingInputStream::Skip(int count) {
  if (count > limit_) {
    if (limit_ < 0) return false;
    input_->Skip(limit_);
    limit_ = 0;
    return false;
  } else {
    if (!input_->Skip(count)) return false;
    limit_ -= count;
    return true;
  }
}

int64_t LimitingInputStream::ByteCount() const {
  if (limit_ < 0) {
    return input_->ByteCount() + limit_ - prior_bytes_read_;
  } else {
    return input_->ByteCount() - prior_bytes_read_;
  }
}

bool LimitingInputStream::ReadCord(absl::Cord* cord, int count) {
  if (count <= 0) return true;
  if (count <= limit_) {
    if (!input_->ReadCord(cord, count)) return false;
    limit_ -= count;
    return true;
  }
  input_->ReadCord(cord, limit_);
  limit_ = 0;
  return false;
}


// ===================================================================
CordInputStream::CordInputStream(const absl::Cord* cord)
    : it_(cord->char_begin()),
      length_(cord->size()),
      bytes_remaining_(length_) {
  LoadChunkData();
}

bool CordInputStream::LoadChunkData() {
  if (bytes_remaining_ != 0) {
    absl::string_view sv = absl::Cord::ChunkRemaining(it_);
    data_ = sv.data();
    size_ = available_ = sv.size();
    return true;
  }
  size_ = available_ = 0;
  return false;
}

bool CordInputStream::NextChunk(size_t skip) {
  // `size_ == 0` indicates we're at EOF.
  if (size_ == 0) return false;

  // The caller consumed 'size_ - available_' bytes that are not yet accounted
  // for in the iterator position to get to the start of the next chunk.
  const size_t distance = size_ - available_ + skip;
  absl::Cord::Advance(&it_, distance);
  bytes_remaining_ -= skip;

  return LoadChunkData();
}

bool CordInputStream::Next(const void** data, int* size) {
  if (available_ > 0 || NextChunk(0)) {
    *data = data_ + size_ - available_;
    *size = available_;
    bytes_remaining_ -= available_;
    available_ = 0;
    return true;
  }
  return false;
}

void CordInputStream::BackUp(int count) {
  // Backup is only allowed on last returned chunk from `Next()`.
  ABSL_CHECK_LE(static_cast<size_t>(count), size_ - available_);

  available_ += count;
  bytes_remaining_ += count;
}

bool CordInputStream::Skip(int count) {
  // Short circuit if we stay inside the current chunk.
  if (static_cast<size_t>(count) <= available_) {
    available_ -= count;
    bytes_remaining_ -= count;
    return true;
  }

  // Sanity check the skip count.
  if (static_cast<size_t>(count) <= bytes_remaining_) {
    // Skip to end: do not return EOF condition: skipping into EOF is ok.
    NextChunk(count);
    return true;
  }
  NextChunk(bytes_remaining_);
  return false;
}

int64_t CordInputStream::ByteCount() const {
  return length_ - bytes_remaining_;
}

bool CordInputStream::ReadCord(absl::Cord* cord, int count) {
  // Advance the iterator to the current position
  const size_t used = size_ - available_;
  absl::Cord::Advance(&it_, used);

  // Read the cord, adjusting the iterator position.
  // Make sure to cap at available bytes to avoid hard crashes.
  const size_t n = std::min(static_cast<size_t>(count), bytes_remaining_);
  cord->Append(absl::Cord::AdvanceAndRead(&it_, n));

  // Update current chunk data.
  bytes_remaining_ -= n;
  LoadChunkData();

  return n == static_cast<size_t>(count);
}


CordOutputStream::CordOutputStream(size_t size_hint) : size_hint_(size_hint) {}

CordOutputStream::CordOutputStream(absl::Cord cord, size_t size_hint)
    : cord_(std::move(cord)),
      size_hint_(size_hint),
      state_(cord_.empty() ? State::kEmpty : State::kSteal) {}

CordOutputStream::CordOutputStream(absl::CordBuffer buffer, size_t size_hint)
    : size_hint_(size_hint),
      state_(buffer.length() < buffer.capacity() ? State::kPartial
                                                 : State::kFull),
      buffer_(std::move(buffer)) {}

CordOutputStream::CordOutputStream(absl::Cord cord, absl::CordBuffer buffer,
                                   size_t size_hint)
    : cord_(std::move(cord)),
      size_hint_(size_hint),
      state_(buffer.length() < buffer.capacity() ? State::kPartial
                                                 : State::kFull),
      buffer_(std::move(buffer)) {}

bool CordOutputStream::Next(void** data, int* size) {
  // Use 128 bytes as a minimum buffer size if we don't have any application
  // provided size hints. This number is picked somewhat arbitrary as 'small
  // enough to avoid excessive waste on small data, and large enough to not
  // waste CPU and memory on tiny buffer overhead'.
  // It is worth noting that absent size hints, we pick 'current size' as
  // the default buffer size (capped at max flat size), which means we quickly
  // double the buffer size. This is in contrast to `Cord::Append()` functions
  // accepting strings which use a conservative 10% growth.
  static const size_t kMinBlockSize = 128;

  size_t desired_size, max_size;
  const size_t cord_size = cord_.size() + buffer_.length();
  if (size_hint_ > cord_size) {
    // Try to hit size_hint_ exactly so the caller doesn't receive a larger
    // buffer than indicated, requiring a non-zero call to BackUp() to undo
    // the buffer capacity we returned beyond the indicated size hint.
    desired_size = size_hint_ - cord_size;
    max_size = desired_size;
  } else {
    // We're past the size hint or don't have a size hint.  Try to allocate a
    // block as large as what we have so far, or at least kMinBlockSize bytes.
    // CordBuffer will truncate this to an appropriate size if it is too large.
    desired_size = std::max(cord_size, kMinBlockSize);
    max_size = std::numeric_limits<size_t>::max();
  }

  switch (state_) {
    case State::kSteal:
      // Steal last buffer from Cord if available.
      assert(buffer_.length() == 0);
      buffer_ = cord_.GetAppendBuffer(desired_size);
      break;
    case State::kPartial:
      // Use existing capacity in 'buffer_`
      assert(buffer_.length() < buffer_.capacity());
      break;
    case State::kFull:
      assert(buffer_.length() > 0);
      cord_.Append(std::move(buffer_));
      [[fallthrough]];
    case State::kEmpty:
      assert(buffer_.length() == 0);
      buffer_ = absl::CordBuffer::CreateWithDefaultLimit(desired_size);
      break;
  }

  // Get all available capacity from the buffer.
  absl::Span<char> span = buffer_.available();
  assert(!span.empty());
  *data = span.data();

  // Only hand out up to 'max_size', which is limited if there is a size hint
  // specified, and we have more available than the size hint.
  if (span.size() > max_size) {
    *size = static_cast<int>(max_size);
    buffer_.IncreaseLengthBy(max_size);
    state_ = State::kPartial;
  } else {
    *size = static_cast<int>(span.size());
    buffer_.IncreaseLengthBy(span.size());
    state_ = State::kFull;
  }

  return true;
}

void CordOutputStream::BackUp(int count) {
  // Check if something to do, else state remains unchanged.
  assert(0 <= count && count <= ByteCount());
  if (count == 0) return;

  // Backup() is not supposed to backup beyond last Next() call
  const int buffer_length = static_cast<int>(buffer_.length());
  assert(count <= buffer_length);
  if (count <= buffer_length) {
    buffer_.SetLength(static_cast<size_t>(buffer_length - count));
    state_ = State::kPartial;
  } else {
    buffer_ = {};
    cord_.RemoveSuffix(static_cast<size_t>(count));
    state_ = State::kSteal;
  }
}

int64_t CordOutputStream::ByteCount() const {
  return static_cast<int64_t>(cord_.size() + buffer_.length());
}

bool CordOutputStream::WriteCord(const absl::Cord& cord) {
  cord_.Append(std::move(buffer_));
  cord_.Append(cord);
  state_ = State::kSteal;  // Attempt to utilize existing capacity in `cord'
  return true;
}

absl::Cord CordOutputStream::Consume() {
  cord_.Append(std::move(buffer_));
  state_ = State::kEmpty;
  return std::move(cord_);
}


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

#include "google/protobuf/port_undef.inc"
