xref: /aosp_15_r20/external/pigweed/pw_channel/public/pw_channel/properties.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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 #include <cstdint>
17 #include <type_traits>
18 
19 namespace pw::channel {
20 
21 /// @addtogroup pw_channel
22 /// @{
23 
24 /// Basic properties of a `Channel`. A `Channel` type can convert to any other
25 /// `Channel` for which it supports the required properties. For example, a
26 /// `kReadable` and `kWritable` channel may be passed to an API that only
27 /// requires `kReadable`.
28 enum Property : uint8_t {
29   /// All data is guaranteed to be delivered in order. The channel is closed if
30   /// data is lost.
31   kReliable = 1 << 0,
32 
33   /// The channel supports reading.
34   kReadable = 1 << 1,
35 
36   /// The channel supports writing.
37   kWritable = 1 << 2,
38 
39   /// The channel supports seeking (changing the read/write position).
40   kSeekable = 1 << 3,
41 };
42 
43 /// The type of data exchanged in `Channel` read and write calls. Unlike
44 /// `Property`, `Channels` with different `DataType`s cannot be used
45 /// interchangeably.
46 enum class DataType : uint8_t { kByte = 0, kDatagram = 1 };
47 
48 /// Positions from which to seek.
49 enum Whence : uint8_t {
50   /// Seek from the beginning of the channel. The offset is a direct offset
51   /// into the data.
52   kBeginning,
53 
54   /// Seek from the current position in the channel. The offset is added to
55   /// the current position. Use a negative offset to seek backwards.
56   ///
57   /// Implementations may only support seeking within a limited range from the
58   /// current position.
59   kCurrent,
60 
61   /// Seek from the end of the channel. The offset is added to the end
62   /// position. Use a negative offset to seek backwards from the end.
63   kEnd,
64 };
65 
66 /// @}
67 
68 template <DataType kDataType, Property... kProperties>
69 class Channel;
70 
71 class AnyChannel;
72 
73 namespace internal {
74 
75 template <DataType kDataType, Property... kProperties>
76 class BaseChannelImpl;
77 
78 template <typename>
79 struct IsChannel : public std::false_type {};
80 
81 template <DataType kDataType, Property... kProperties>
82 struct IsChannel<Channel<kDataType, kProperties...>> : public std::true_type {};
83 
84 // Returns whether a sibling channel supports the required properties.
85 template <typename Self, typename Sibling>
86 using EnableIfConversionIsValid =
87     std::enable_if_t<  // Sibling type must be a Channel
88         std::is_same_v<Sibling, AnyChannel> ||
89         (IsChannel<Sibling>::value &&
90          // Datagram and byte channels are not interchangeable
91          (Sibling::data_type() == Self::data_type()) &&
92          // Cannot use a unreliable channel as a reliable channel
93          (!Sibling::reliable() || Self::reliable()) &&
94          // Cannot use a non-readable channel as a readable channel
95          (!Sibling::readable() || Self::readable()) &&
96          // Cannot use a non-writable channel as a writable channel
97          (!Sibling::writable() || Self::writable()) &&
98          // Cannot use a non-seekable channel as a seekable channel
99          (!Sibling::seekable() || Self::seekable()))>;
100 
101 // Performs the same checks as EnableIfConversionIsValid, but generates a
102 // static_assert with a helpful message if any condition is not met.
103 template <typename Self, typename Sibling>
104 static constexpr void CheckThatConversionIsValid() {
105   if constexpr (!std::is_same_v<Sibling, AnyChannel>) {
106     static_assert(IsChannel<Sibling>::value,
107                   "Only conversions to other Channel types are supported");
108     static_assert(Sibling::data_type() == Self::data_type(),
109                   "Datagram and byte channels are not interchangeable");
110     static_assert(!Sibling::reliable() || Self::reliable(),
111                   "Cannot use a unreliable channel as a reliable channel");
112     static_assert(!Sibling::readable() || Self::readable(),
113                   "Cannot use a non-readable channel as a readable channel");
114     static_assert(!Sibling::writable() || Self::writable(),
115                   "Cannot use a non-writable channel as a writable channel");
116     static_assert(!Sibling::seekable() || Self::seekable(),
117                   "Cannot use a non-seekable channel as a seekable channel");
118   }
119 }
120 
121 template <Property>
122 constexpr bool PropertiesAreInOrderWithoutDuplicates() {
123   return true;
124 }
125 
126 template <Property kLhs, Property kRhs, Property... kOthers>
127 constexpr bool PropertiesAreInOrderWithoutDuplicates() {
128   return (kLhs < kRhs) &&
129          PropertiesAreInOrderWithoutDuplicates<kRhs, kOthers...>();
130 }
131 
132 template <Property... kProperties>
133 static constexpr bool PropertiesAreValid() {
134   static_assert(((kProperties != kSeekable) && ...),
135                 "Seekable channels are not yet implemented; see b/323624921");
136 
137   static_assert(((kProperties == kReadable) || ...) ||
138                     ((kProperties == kWritable) || ...),
139                 "At least one of kReadable or kWritable must be provided");
140   static_assert(sizeof...(kProperties) <= 4,
141                 "Too many properties given; no more than 4 may be specified "
142                 "(kReliable, kReadable, kWritable, kSeekable)");
143   static_assert(PropertiesAreInOrderWithoutDuplicates<kProperties...>(),
144                 "Properties must be specified in the following order, without "
145                 "duplicates: kReliable, kReadable, kWritable, kSeekable");
146   return true;
147 }
148 
149 }  // namespace internal
150 }  // namespace pw::channel
151