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