// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "services/network/public/cpp/net_adapters.h"

#include <limits>

#include "base/check_op.h"
#include "base/containers/span.h"
#include "base/strings/string_view_util.h"
#include "net/base/net_errors.h"

namespace network {

namespace {
constexpr size_t kMaxBufSize = 64 * 1024;
}

NetToMojoPendingBuffer::NetToMojoPendingBuffer(
    mojo::ScopedDataPipeProducerHandle handle,
    base::span<char> buffer)
    : handle_(std::move(handle)), buffer_(buffer) {
  CHECK_LE(buffer_.size(), std::numeric_limits<uint32_t>::max());
}

NetToMojoPendingBuffer::~NetToMojoPendingBuffer() {
  if (handle_.is_valid())
    handle_->EndWriteData(0);
}

MojoResult NetToMojoPendingBuffer::BeginWrite(
    mojo::ScopedDataPipeProducerHandle* handle,
    scoped_refptr<NetToMojoPendingBuffer>* pending) {
  base::span<uint8_t> buf;
  MojoResult result =
      (*handle)->BeginWriteData(kMaxBufSize, MOJO_WRITE_DATA_FLAG_NONE, buf);
  if (result != MOJO_RESULT_OK) {
    *pending = nullptr;
    return result;
  }
  if (buf.size() > kMaxBufSize) {
    buf = buf.first(kMaxBufSize);
  }
  *pending = new NetToMojoPendingBuffer(std::move(*handle),
                                        base::as_writable_chars(buf));
  return MOJO_RESULT_OK;
}

mojo::ScopedDataPipeProducerHandle NetToMojoPendingBuffer::Complete(
    uint32_t num_bytes) {
  handle_->EndWriteData(num_bytes);
  buffer_ = base::span<char>();
  return std::move(handle_);
}

NetToMojoIOBuffer::NetToMojoIOBuffer(
    scoped_refptr<NetToMojoPendingBuffer> pending_buffer,
    size_t offset)
    : net::WrappedIOBuffer(base::span(*pending_buffer).subspan(offset)),
      pending_buffer_(std::move(pending_buffer)) {}

NetToMojoIOBuffer::~NetToMojoIOBuffer() {
  // Avoid dangling ptr should this destructor remove the last reference
  // to `pending_buffer_`.
  ClearSpan();
}

MojoToNetPendingBuffer::MojoToNetPendingBuffer(
    mojo::ScopedDataPipeConsumerHandle handle,
    base::span<const char> buffer)
    : handle_(std::move(handle)), buffer_(buffer) {
  CHECK_LE(buffer_.size(), std::numeric_limits<uint32_t>::max());
}

MojoToNetPendingBuffer::~MojoToNetPendingBuffer() = default;

// static
MojoResult MojoToNetPendingBuffer::BeginRead(
    mojo::ScopedDataPipeConsumerHandle* handle,
    scoped_refptr<MojoToNetPendingBuffer>* pending) {
  base::span<const uint8_t> buffer;
  MojoResult result =
      (*handle)->BeginReadData(MOJO_READ_DATA_FLAG_NONE, buffer);
  if (result != MOJO_RESULT_OK) {
    *pending = nullptr;
    return result;
  }
  *pending = new MojoToNetPendingBuffer(std::move(*handle),
                                        base::as_string_view(buffer));
  return MOJO_RESULT_OK;
}

void MojoToNetPendingBuffer::CompleteRead(uint32_t num_bytes) {
  handle_->EndReadData(num_bytes);
  buffer_ = base::span<const char>();
}

mojo::ScopedDataPipeConsumerHandle MojoToNetPendingBuffer::ReleaseHandle() {
  DCHECK(IsComplete());
  return std::move(handle_);
}

bool MojoToNetPendingBuffer::IsComplete() const {
  return buffer_.empty();
}

MojoToNetIOBuffer::MojoToNetIOBuffer(
    scoped_refptr<MojoToNetPendingBuffer> pending_buffer,
    int bytes_to_be_read)
    : net::WrappedIOBuffer(*pending_buffer),
      pending_buffer_(std::move(pending_buffer)),
      bytes_to_be_read_(bytes_to_be_read) {}

MojoToNetIOBuffer::~MojoToNetIOBuffer() {
  // We can safely notify mojo, that the data has been consumed and can be
  // released at this point.
  pending_buffer_->CompleteRead(bytes_to_be_read_);

  // Prevent dangling ptr should this destructor remove the last reference
  // to `pending_buffer_`.
  ClearSpan();
}

}  // namespace network
