910e62b5创建于 1月15日历史提交
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// This file provides a C++ wrapping around the Mojo C API for data pipes,
// replacing the prefix of "Mojo" with a "mojo" namespace, and using more
// strongly-typed representations of |MojoHandle|s.
//
// Please see "mojo/public/c/system/data_pipe.h" for complete documentation of
// the API.

#ifndef MOJO_PUBLIC_CPP_SYSTEM_DATA_PIPE_H_
#define MOJO_PUBLIC_CPP_SYSTEM_DATA_PIPE_H_

#include <stddef.h>
#include <stdint.h>

#include "base/containers/span.h"
#include "base/numerics/safe_conversions.h"
#include "mojo/public/c/system/data_pipe.h"
#include "mojo/public/c/system/types.h"
#include "mojo/public/cpp/system/handle.h"

namespace mojo {

// A strongly-typed representation of a |MojoHandle| to the producer end of a
// data pipe.
class DataPipeProducerHandle : public Handle {
 public:
  DataPipeProducerHandle() {}
  explicit DataPipeProducerHandle(MojoHandle value) : Handle(value) {}

  // Writes to a data pipe. See |MojoWriteData| for complete documentation.
  //
  // `data` points to the buffer with the data to write.  `data` bigger than
  // 2^32 bytes may result in `MOJO_RESULT_INVALID_ARGUMENT` unless
  // `MojoCreateDataPipeOptions::element_num_bytes` is 1.
  //
  // `bytes_written` is an out parameter that on `MOJO_RESULT_OK` communicates
  // how many bytes (from the beginning of `data`) were actually written into
  // the mojo data pipe (depending on the size of the internal pipe buffer, this
  // may be less than `data.size()`).
  //
  // Note that instead of passing specific `flags`, a more direct method can be
  // used instead:
  // - `MOJO_WRITE_DATA_FLAG_ALL_OR_NONE` => `WriteAllData`
  MojoResult WriteData(base::span<const uint8_t> data,
                       MojoWriteDataFlags flags,
                       size_t& bytes_written) const {
    MojoWriteDataOptions options;
    options.struct_size = sizeof(options);
    options.flags = flags;

    // Because of ABI-stability requirements, the C-level APIs take `uint32_t`.
    // But, for compatibility with C++ containers, the C++ APIs take `size_t`.
    //
    // We use `saturated_cast` so that when `num_bytes` doesn't fit into
    // `uint32_t`, then we will simply report that a smaller number of bytes was
    // written.  We accept that `num_bytes_u32` may no longer being a multiple
    // of `MojoCreateDataPipeOptions::element_num_bytes` and rely on the C layer
    // to return `MOJO_RESULT_INVALID_ARGUMENT` in this case (as reflected in
    // the doc comment above).
    uint32_t num_bytes_u32 = base::saturated_cast<uint32_t>(data.size());

    MojoResult result =
        MojoWriteData(value(), data.data(), &num_bytes_u32, &options);
    bytes_written = size_t{num_bytes_u32};
    return result;
  }

  MojoResult WriteAllData(base::span<const uint8_t> data) const {
    // Ok to ignore `bytes_written` because `MOJO_WRITE_DATA_FLAG_ALL_OR_NONE`
    // means that `MOJO_RESULT_OK` will be returned only if exactly
    // `data.size()` bytes have been written (i.e. if `data.size() ==
    // bytes_written`).
    size_t bytes_written = 0;
    return WriteData(data, MOJO_WRITE_DATA_FLAG_ALL_OR_NONE, bytes_written);
  }

  // Using `kNoSizeHint` as `write_size_hint` argument of `BeginWriteData`
  // conveys that the caller doesn't know (or care) how much data needs to be
  // written into the buffer.
  static constexpr size_t kNoSizeHint = 0;

  // Begins a two-phase write to a data pipe. See |MojoBeginWriteData()| for
  // complete documentation.
  //
  // `buffer` is an out parameter that on `MOJO_RESULT_OK` points to a mutable,
  // pipe-owned buffer, that the caller can write into.  The caller should
  // call `EndWriteData` when they are done writing into the `buffer`.
  MojoResult BeginWriteData(size_t write_size_hint,
                            MojoBeginWriteDataFlags flags,
                            base::span<uint8_t>& buffer) const {
    MojoBeginWriteDataOptions options;
    options.struct_size = sizeof(options);
    options.flags = flags;

    // Because of ABI-stability requirements, the C-level APIs take `uint32_t`.
    // But, for compatibility with C++ containers, the C++ APIs take `size_t`.
    //
    // As documented by MojoBeginWriteData, on input `write_size_hint` is
    // merely a hint of how many bytes the producer is readily able to supply.
    // Therefore we use a `saturated_cast` to gracefully handle big values.
    void* buffer_ptr = nullptr;
    uint32_t buffer_num_bytes = base::saturated_cast<uint32_t>(write_size_hint);
    MojoResult result =
        MojoBeginWriteData(value(), &options, &buffer_ptr, &buffer_num_bytes);
    if (result == MOJO_RESULT_OK) {
      // SAFETY: Relying on the contract of the `MojoBeginWriteData` C API which
      // says: "On success |*buffer| will be a pointer to which the caller can
      // write up to |*buffer_num_bytes| bytes of data."
      buffer = UNSAFE_BUFFERS(base::span(static_cast<uint8_t*>(buffer_ptr),
                                         size_t{buffer_num_bytes}));
    }
    return result;
  }

  // Completes a two-phase write to a data pipe. See |MojoEndWriteData()| for
  // complete documentation.
  MojoResult EndWriteData(size_t num_bytes_written) const {
    if (!base::IsValueInRangeForNumericType<uint32_t>(num_bytes_written))
        [[unlikely]] {
      return MOJO_RESULT_INVALID_ARGUMENT;
    }
    uint32_t num_bytes_written_u32 =
        base::checked_cast<uint32_t>(num_bytes_written);

    return MojoEndWriteData(value(), num_bytes_written_u32, nullptr);
  }

  // Copying and assignment allowed.
};

static_assert(sizeof(DataPipeProducerHandle) == sizeof(Handle),
              "Bad size for C++ DataPipeProducerHandle");

typedef ScopedHandleBase<DataPipeProducerHandle> ScopedDataPipeProducerHandle;
static_assert(sizeof(ScopedDataPipeProducerHandle) ==
                  sizeof(DataPipeProducerHandle),
              "Bad size for C++ ScopedDataPipeProducerHandle");

// A strongly-typed representation of a |MojoHandle| to the consumer end of a
// data pipe.
class DataPipeConsumerHandle : public Handle {
 public:
  DataPipeConsumerHandle() {}
  explicit DataPipeConsumerHandle(MojoHandle value) : Handle(value) {}

  // Reads from a data pipe. See |MojoReadData()| for complete documentation.
  //
  // `buffer` points to the buffer where `ReadData` should copy the read bytes.
  //
  // `bytes_read` is an out parameter that on `MOJO_RESULT_OK` communicates how
  // many bytes (at the beginning of `buffer`) were actually read (depending on
  // the size of the internal pipe buffer, this may be less than
  // `buffer.size()`).
  //
  // Note that instead of passing specific `flags`, a more direct method can be
  // used instead:
  // - `MOJO_READ_DATA_FLAG_DISCARD` => `DiscardData`
  MojoResult ReadData(MojoReadDataFlags flags,
                      base::span<uint8_t> buffer,
                      size_t& bytes_read) const {
    MojoReadDataOptions options;
    options.struct_size = sizeof(options);
    options.flags = flags;

    // Because of ABI-stability requirements, the C-level APIs take `uint32_t`.
    // But, for compatibility with C++ containers, the C++ APIs take `size_t`.
    //
    // Input value of `*num_bytes` is ignored in `MOJO_READ_DATA_FLAG_QUERY`
    // mode and otherwise is an _upper_ bound on how many bytes will be read (or
    // discarded in `MOJO_READ_DATA_FLAG_DISCARD`).  Therefore it is okay to use
    // `saturated_cast` instead of `checked_cast` because the C-layer mojo code
    // will anyway read only up to uin32_t max bytes.
    uint32_t num_bytes = base::saturated_cast<uint32_t>(buffer.size());
    MojoResult result =
        MojoReadData(value(), &options, buffer.data(), &num_bytes);
    bytes_read = size_t{num_bytes};
    return result;
  }

  // Discards data from a data pipe. See |MojoReadData()| and
  // |MOJO_READ_DATA_FLAG_DISCARD| for complete documentation.
  //
  // `bytes_to_discard` is an input parameter that specifies how many bytes from
  // the pipe should be read and discarded.
  //
  // `bytes_discarded` is an output parameter that on `MOJO_RESULT_OK` specifies
  // how many bytes were actually discarded.
  MojoResult DiscardData(size_t bytes_to_discard,
                         size_t& bytes_discarded) const {
    MojoReadDataOptions options;
    options.struct_size = sizeof(options);
    options.flags = MOJO_READ_DATA_FLAG_DISCARD;

    // Because of ABI-stability requirements, the C-level APIs take `uint32_t`.
    // But, for compatibility with C++ containers, the C++ APIs take `size_t`.
    //
    // Since the underlying C-API can always discard less bytes than requested,
    // we can use `saturated_cast` below.
    uint32_t num_bytes = base::saturated_cast<uint32_t>(bytes_to_discard);
    MojoResult result = MojoReadData(value(), &options, nullptr, &num_bytes);
    bytes_discarded = size_t{num_bytes};
    return result;
  }

  // Begins a two-phase read from a data pipe. See |MojoBeginReadData()| for
  // complete documentation.
  //
  // `buffer` is an out parameter that on `MOJO_RESULT_OK` points to a
  // read-only, pipe-owned buffer, that the caller can read from.  The caller
  // should call `EndReadData` when they are done reading from the `buffer`.
  MojoResult BeginReadData(MojoBeginReadDataFlags flags,
                           base::span<const uint8_t>& buffer) const {
    MojoBeginReadDataOptions options;
    options.struct_size = sizeof(options);
    options.flags = flags;

    const void* data = nullptr;
    uint32_t buffer_num_bytes = 0;
    MojoResult result =
        MojoBeginReadData(value(), &options, &data, &buffer_num_bytes);
    if (result == MOJO_RESULT_OK) {
      // SAFETY: Relying on the contract of the `MojoBeginReadData` C API which
      // says: "On success, |*buffer| will be a pointer from which the caller
      // can read up to |*buffer_num_bytes| bytes of data."
      buffer = UNSAFE_BUFFERS(base::span(static_cast<const uint8_t*>(data),
                                         size_t{buffer_num_bytes}));
    }
    return result;
  }

  // Completes a two-phase read from a data pipe. See |MojoEndReadData()| for
  // complete documentation.
  MojoResult EndReadData(size_t num_bytes_read) const {
    if (!base::IsValueInRangeForNumericType<uint32_t>(num_bytes_read))
        [[unlikely]] {
      return MOJO_RESULT_INVALID_ARGUMENT;
    }
    uint32_t num_bytes_read_u32 = base::checked_cast<uint32_t>(num_bytes_read);

    return MojoEndReadData(value(), num_bytes_read_u32, nullptr);
  }

  // Copying and assignment allowed.
};

static_assert(sizeof(DataPipeConsumerHandle) == sizeof(Handle),
              "Bad size for C++ DataPipeConsumerHandle");

typedef ScopedHandleBase<DataPipeConsumerHandle> ScopedDataPipeConsumerHandle;
static_assert(sizeof(ScopedDataPipeConsumerHandle) ==
                  sizeof(DataPipeConsumerHandle),
              "Bad size for C++ ScopedDataPipeConsumerHandle");

// Creates a new data pipe. See |MojoCreateDataPipe()| for complete
// documentation.
inline MojoResult CreateDataPipe(
    const MojoCreateDataPipeOptions* options,
    ScopedDataPipeProducerHandle& data_pipe_producer,
    ScopedDataPipeConsumerHandle& data_pipe_consumer) {
  DataPipeProducerHandle producer_handle;
  DataPipeConsumerHandle consumer_handle;
  MojoResult rv = MojoCreateDataPipe(options,
                                     producer_handle.mutable_value(),
                                     consumer_handle.mutable_value());
  // Reset even on failure (reduces the chances that a "stale"/incorrect handle
  // will be used).
  data_pipe_producer.reset(producer_handle);
  data_pipe_consumer.reset(consumer_handle);
  return rv;
}

// Creates a new data pipe with a specified capacity size. For setting
// additional options, see |CreateDataPipe()| above.
inline MojoResult CreateDataPipe(
    uint32_t capacity_num_bytes,
    ScopedDataPipeProducerHandle& data_pipe_producer,
    ScopedDataPipeConsumerHandle& data_pipe_consumer) {
  MojoCreateDataPipeOptions options;
  options.struct_size = sizeof(MojoCreateDataPipeOptions);
  options.flags = MOJO_CREATE_DATA_PIPE_FLAG_NONE;
  options.element_num_bytes = 1;
  options.capacity_num_bytes = capacity_num_bytes;
  return CreateDataPipe(&options, data_pipe_producer, data_pipe_consumer);
}

}  // namespace mojo

#endif  // MOJO_PUBLIC_CPP_SYSTEM_DATA_PIPE_H_