blob: fcca87b3633dda8f03b363da618a5685b2b97d98 [file] [log] [blame]
// 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_