1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
|
// 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_
|