Pigweed
 
Loading...
Searching...
No Matches
channel.h
1// Copyright 2024 The Pigweed Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not
4// use this file except in compliance with the License. You may obtain a copy of
5// the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations under
13// the License.
14#pragma once
15
16// __ ___ ___ _ _ ___ _ _ ___
17// \ \ / /_\ | _ \ \| |_ _| \| |/ __|
18// \ \/\/ / _ \| / .` || || .` | (_ |
19// \_/\_/_/ \_\_|_\_|\_|___|_|\_|\___|
20// _____ _____ ___ ___ ___ __ __ ___ _ _ _____ _ _
21// | __\ \/ / _ \ __| _ \_ _| \/ | __| \| |_ _/_\ | |
22// | _| > <| _/ _|| /| || |\/| | _|| .` | | |/ _ \| |__
23// |___/_/\_\_| |___|_|_\___|_| |_|___|_|\_| |_/_/ \_\____|
24//
25// This module is in an early, experimental state. The APIs are in flux and may
26// change without notice. Please do not rely on it in production code, but feel
27// free to explore and share feedback with the Pigweed team!
28
29#include <cstddef>
30#include <cstdint>
31#include <limits>
32#include <utility>
33
34#include "pw_async2/dispatcher.h"
35#include "pw_async2/poll.h"
36#include "pw_bytes/span.h"
37#include "pw_channel/properties.h"
38#include "pw_multibuf/multibuf.h"
39#include "pw_result/result.h"
40#include "pw_status/status.h"
41
42namespace pw::channel {
43
46
55template <DataType kDataType, Property... kProperties>
56class Channel {
57 public:
59 [[nodiscard]] static constexpr DataType data_type() { return kDataType; }
60
62 [[nodiscard]] static constexpr bool reliable() {
63 return ((kProperties == Property::kReliable) || ...);
64 }
66 [[nodiscard]] static constexpr bool seekable() {
67 return ((kProperties == Property::kSeekable) || ...);
68 }
70 [[nodiscard]] static constexpr bool readable() {
71 return ((kProperties == Property::kReadable) || ...);
72 }
74 [[nodiscard]] static constexpr bool writable() {
75 return ((kProperties == Property::kWritable) || ...);
76 }
77
80 [[nodiscard]] constexpr bool is_read_open() const;
81
84 [[nodiscard]] constexpr bool is_write_open() const;
85
87 [[nodiscard]] constexpr bool is_read_or_write_open() const {
88 return is_read_open() || is_write_open();
89 }
90
97 async2::Context& cx, size_t min_bytes);
104
105 // Conversions
106
108 template <typename Sibling,
109 typename = internal::EnableIfConversionIsValid<Channel, Sibling>>
110 constexpr operator Sibling&() {
111 return as<Sibling>();
112 }
113 template <typename Sibling,
114 typename = internal::EnableIfConversionIsValid<Channel, Sibling>>
115 constexpr operator const Sibling&() const {
116 return as<Sibling>();
117 }
118
120 template <typename Sibling>
121 [[nodiscard]] Sibling& as() {
122 internal::CheckThatConversionIsValid<Channel, Sibling>();
123 return static_cast<Sibling&>(static_cast<AnyChannel&>(*this));
124 }
125 template <typename Sibling>
126 [[nodiscard]] const Sibling& as() const {
127 internal::CheckThatConversionIsValid<Channel, Sibling>();
128 return static_cast<const Sibling&>(static_cast<const AnyChannel&>(*this));
129 }
130
133 template <Property... kOtherChannelProperties>
134 [[nodiscard]] auto& as() {
135 return as<Channel<data_type(), kOtherChannelProperties...>>();
136 }
137 template <Property... kOtherChannelProperties>
138 [[nodiscard]] const auto& as() const {
139 return as<Channel<data_type(), kOtherChannelProperties...>>();
140 }
141
142 [[nodiscard]] Channel<DataType::kByte, kProperties...>&
143 IgnoreDatagramBoundaries() {
144 static_assert(kDataType == DataType::kDatagram,
145 "IgnoreDatagramBoundaries() may only be called to use a "
146 "datagram channel to a byte channel");
147 return static_cast<Channel<DataType::kByte, kProperties...>&>(
148 static_cast<AnyChannel&>(*this));
149 }
150
151 [[nodiscard]] const Channel<DataType::kByte, kProperties...>&
152 IgnoreDatagramBoundaries() const {
153 static_assert(kDataType == DataType::kDatagram,
154 "IgnoreDatagramBoundaries() may only be called to use a "
155 "datagram channel to a byte channel");
156 return static_cast<const Channel<DataType::kByte, kProperties...>&>(
157 static_cast<const AnyChannel&>(*this));
158 }
159
160 private:
161 static_assert(internal::PropertiesAreValid<kProperties...>());
162
163 friend class AnyChannel;
164
165 explicit constexpr Channel() = default;
166};
167
183 : private Channel<DataType::kByte, kReadable>,
184 private Channel<DataType::kByte, kWritable>,
185 private Channel<DataType::kByte, kReadable, kWritable>,
186 private Channel<DataType::kByte, kReliable, kReadable>,
187 private Channel<DataType::kByte, kReliable, kWritable>,
188 private Channel<DataType::kByte, kReliable, kReadable, kWritable>,
189 private Channel<DataType::kDatagram, kReadable>,
190 private Channel<DataType::kDatagram, kWritable>,
191 private Channel<DataType::kDatagram, kReadable, kWritable>,
192 private Channel<DataType::kDatagram, kReliable, kReadable>,
193 private Channel<DataType::kDatagram, kReliable, kWritable>,
194 private Channel<DataType::kDatagram, kReliable, kReadable, kWritable> {
195 public:
196 virtual ~AnyChannel() = default;
197
198 // Returned by Position() if getting the position is not supported.
199 // TODO: b/323622630 - `Seek` and `Position` are not yet implemented.
200 // static constexpr size_t kUnknownPosition =
201 // std::numeric_limits<size_t>::max();
202
203 // Channel properties
204
206 [[nodiscard]] constexpr DataType data_type() const { return data_type_; }
207
209 [[nodiscard]] constexpr bool reliable() const {
210 return (properties_ & Property::kReliable) != 0;
211 }
212
214 [[nodiscard]] constexpr bool seekable() const {
215 return (properties_ & Property::kSeekable) != 0;
216 }
217
219 [[nodiscard]] constexpr bool readable() const {
220 return (properties_ & Property::kReadable) != 0;
221 }
222
224 [[nodiscard]] constexpr bool writable() const {
225 return (properties_ & Property::kWritable) != 0;
226 }
227
230 [[nodiscard]] constexpr bool is_read_open() const { return read_open_; }
231
234 [[nodiscard]] constexpr bool is_write_open() const { return write_open_; }
235
237 [[nodiscard]] constexpr bool is_read_or_write_open() const {
238 return read_open_ || write_open_;
239 }
240
241 // Read API
242
269 if (!is_read_open()) {
270 return Status::FailedPrecondition();
271 }
272 async2::Poll<Result<multibuf::MultiBuf>> result = DoPendRead(cx);
273 if (result.IsReady() && result->status().IsFailedPrecondition()) {
274 set_read_closed();
275 }
276 return result;
277 }
278
279 // Write API
280
298 if (!is_write_open()) {
299 return Status::FailedPrecondition();
300 }
301 async2::Poll<Status> result = DoPendReadyToWrite(cx);
302 if (result.IsReady() && result->IsFailedPrecondition()) {
303 set_write_closed();
304 }
305 return result;
306 }
307
329 async2::Context& cx, size_t min_bytes) {
330 return DoPendAllocateWriteBuffer(cx, min_bytes);
331 }
332
365 if (!is_write_open()) {
366 return Status::FailedPrecondition();
367 }
368 Status status = DoStageWrite(std::move(data));
369 if (status.IsFailedPrecondition()) {
370 set_write_closed();
371 }
372 return status;
373 }
374
384 if (!is_write_open()) {
385 return Status::FailedPrecondition();
386 }
387 async2::Poll<Status> status = DoPendWrite(cx);
388 if (status.IsReady() && status->IsFailedPrecondition()) {
389 set_write_closed();
390 }
391 return status;
392 }
393
419 Status Seek(async2::Context& cx, ptrdiff_t position, Whence whence);
420
425 size_t Position() const;
426
443 if (!is_read_or_write_open()) {
444 return Status::FailedPrecondition();
445 }
446 auto result = DoPendClose(cx);
447 if (result.IsReady()) {
448 set_read_closed();
449 set_write_closed();
450 }
451 return result;
452 }
453
454 protected:
455 // Marks the channel as closed for reading, but does nothing else.
456 //
457 // PendClose() always marks the channel closed when DoPendClose() returns
458 // Ready(), regardless of the status.
459 void set_read_closed() { read_open_ = false; }
460
461 // Marks the channel as closed for writing, but does nothing else.
462 //
463 // PendClose() always marks the channel closed when DoPendClose() returns
464 // Ready(), regardless of the status.
465 void set_write_closed() { write_open_ = false; }
466
467 private:
468 template <DataType, Property...>
469 friend class Channel; // Allow static_casts to AnyChannel
470
471 template <DataType, Property...>
472 friend class internal::BaseChannelImpl; // Allow inheritance
473
474 // `AnyChannel` may only be constructed by deriving from `ChannelImpl`.
475 explicit constexpr AnyChannel(DataType type, uint8_t properties)
476 : data_type_(type),
477 properties_(properties),
478 read_open_(readable()),
479 write_open_(writable()) {}
480
481 // Virtual interface
482
483 // Read functions
484
485 virtual async2::Poll<Result<multibuf::MultiBuf>> DoPendRead(
486 async2::Context& cx) = 0;
487
488 // Write functions
489
490 virtual async2::Poll<std::optional<multibuf::MultiBuf>>
491 DoPendAllocateWriteBuffer(async2::Context& cx, size_t min_bytes) = 0;
492
493 virtual pw::async2::Poll<Status> DoPendReadyToWrite(async2::Context& cx) = 0;
494
495 virtual Status DoStageWrite(multibuf::MultiBuf&& buffer) = 0;
496
497 virtual pw::async2::Poll<Status> DoPendWrite(async2::Context& cx) = 0;
498
499 // Seek functions
501
502 // virtual Status DoSeek(ptrdiff_t position, Whence whence) = 0;
503
504 // virtual size_t DoPosition() const = 0;
505
506 // Common functions
508
509 DataType data_type_;
510 uint8_t properties_;
511 bool read_open_;
512 bool write_open_;
513};
514
516template <Property... kProperties>
517using ByteChannel = Channel<DataType::kByte, kProperties...>;
518
520template <Property... kProperties>
521using DatagramChannel = Channel<DataType::kDatagram, kProperties...>;
522
529
536
543
551
552// Implementation extension classes.
553
563template <DataType kDataType, Property... kProperties>
565 static_assert(internal::PropertiesAreValid<kProperties...>());
566};
567
569template <Property... kProperties>
570using ByteChannelImpl = ChannelImpl<DataType::kByte, kProperties...>;
571
573template <Property... kProperties>
574using DatagramChannelImpl = ChannelImpl<DataType::kDatagram, kProperties...>;
575
582template <typename ChannelType>
584
586
587template <DataType kDataType, Property... kProperties>
588class Implement<Channel<kDataType, kProperties...>>
589 : public ChannelImpl<kDataType, kProperties...> {
590 protected:
591 explicit constexpr Implement() = default;
592};
593
594} // namespace pw::channel
595
596// Include specializations for supported Channel types.
597#include "pw_channel/internal/channel_specializations.h"
Definition: status.h:85
Definition: dispatcher_base.h:52
Definition: poll.h:54
constexpr bool IsReady() const noexcept
Returns whether or not this value is Ready.
Definition: poll.h:125
Definition: channel.h:194
constexpr bool readable() const
Returns whether the channel implementation is readable.
Definition: channel.h:219
async2::Poll< pw::Status > PendClose(async2::Context &cx)
Definition: channel.h:442
async2::Poll< Status > PendWrite(async2::Context &cx)
Definition: channel.h:383
async2::Poll< Status > PendReadyToWrite(pw::async2::Context &cx)
Definition: channel.h:297
constexpr bool is_read_or_write_open() const
True if the channel is open for either reading or writing.
Definition: channel.h:237
async2::Poll< Result< multibuf::MultiBuf > > PendRead(async2::Context &cx)
Definition: channel.h:268
Status Seek(async2::Context &cx, ptrdiff_t position, Whence whence)
async2::Poll< std::optional< multibuf::MultiBuf > > PendAllocateWriteBuffer(async2::Context &cx, size_t min_bytes)
Definition: channel.h:328
constexpr bool reliable() const
Returns whether the channel implementation is reliable.
Definition: channel.h:209
constexpr bool writable() const
Returns whether the channel implementation is writable.
Definition: channel.h:224
size_t Position() const
constexpr bool is_read_open() const
Definition: channel.h:230
Status StageWrite(multibuf::MultiBuf &&data)
Definition: channel.h:364
constexpr bool is_write_open() const
Definition: channel.h:234
constexpr bool seekable() const
Returns whether the channel implementation is seekable.
Definition: channel.h:214
virtual async2::Poll< Status > DoPendClose(async2::Context &cx)=0
TODO: b/323622630 - Seek and Position are not yet implemented.
constexpr DataType data_type() const
Returns the data type of the channel implementation.
Definition: channel.h:206
Definition: channel.h:56
async2::Poll< Status > PendWrite(async2::Context &cx)
constexpr bool is_read_open() const
async2::Poll< Result< multibuf::MultiBuf > > PendRead(async2::Context &cx)
async2::Poll< std::optional< multibuf::MultiBuf > > PendAllocateWriteBuffer(async2::Context &cx, size_t min_bytes)
static constexpr bool seekable()
Returns whether the channel type is seekable.
Definition: channel.h:66
static constexpr bool readable()
Returns whether the channel type is readable.
Definition: channel.h:70
constexpr bool is_write_open() const
Sibling & as()
Returns a reference to this channel as another compatible channel type.
Definition: channel.h:121
static constexpr bool reliable()
Returns whether the channel type is reliable.
Definition: channel.h:62
auto & as()
Definition: channel.h:134
constexpr bool is_read_or_write_open() const
True if the channel is open for reading or writing.
Definition: channel.h:87
async2::Poll< Status > PendReadyToWrite(pw::async2::Context &cx)
Status StageWrite(multibuf::MultiBuf &&data)
async2::Poll< pw::Status > PendClose(async2::Context &cx)
static constexpr DataType data_type()
Returns the data type of this channel.
Definition: channel.h:59
static constexpr bool writable()
Returns whether the channel type is writable.
Definition: channel.h:74
Definition: channel.h:564
Definition: channel.h:583
Definition: multibuf.h:245