1 // Copyright 2020 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 <cstddef> 17 #include <cstdint> 18 19 #include "pw_bytes/span.h" 20 #include "pw_protobuf/serialized_size.h" 21 #include "pw_rpc/internal/packet.pwpb.h" 22 #include "pw_span/span.h" 23 #include "pw_status/status_with_size.h" 24 25 namespace pw::rpc::internal { 26 27 class Packet { 28 public: 29 static constexpr uint32_t kUnassignedId = 0; 30 31 // TODO: b/236156534 - This can use the pwpb generated 32 // pw::rpc::internal::pwpb::RpcPacket::kMaxEncodedSizeBytes once the max value 33 // of enums is properly accounted for and when `status` is changed from a 34 // uint32 to a StatusCode. 35 static constexpr size_t kMinEncodedSizeWithoutPayload = 36 protobuf::SizeOfFieldEnum(pwpb::RpcPacket::Fields::kType, 7) + 37 protobuf::SizeOfFieldUint32(pwpb::RpcPacket::Fields::kChannelId) + 38 protobuf::SizeOfFieldFixed32(pwpb::RpcPacket::Fields::kServiceId) + 39 protobuf::SizeOfFieldFixed32(pwpb::RpcPacket::Fields::kMethodId) + 40 protobuf::SizeOfDelimitedFieldWithoutValue( 41 pwpb::RpcPacket::Fields::kPayload) + 42 protobuf::SizeOfFieldUint32(pwpb::RpcPacket::Fields::kStatus, 43 Status::Unauthenticated().code()) + 44 protobuf::SizeOfFieldUint32(pwpb::RpcPacket::Fields::kCallId); 45 46 // Parses a packet from a protobuf message. Missing or malformed fields take 47 // their default values. 48 static Result<Packet> FromBuffer(ConstByteSpan data); 49 50 // Creates an RPC packet with the channel, service, and method ID of the 51 // provided packet. 52 static constexpr Packet Response(const Packet& request, 53 Status status = OkStatus()) { 54 return Packet(pwpb::PacketType::RESPONSE, 55 request.channel_id(), 56 request.service_id(), 57 request.method_id(), 58 request.call_id(), 59 {}, 60 status); 61 } 62 63 // Creates a SERVER_ERROR packet with the channel, service, and method ID of 64 // the provided packet. ServerError(const Packet & packet,Status status)65 static constexpr Packet ServerError(const Packet& packet, Status status) { 66 return Packet(pwpb::PacketType::SERVER_ERROR, 67 packet.channel_id(), 68 packet.service_id(), 69 packet.method_id(), 70 packet.call_id(), 71 {}, 72 status); 73 } 74 75 // Creates a CLIENT_ERROR packet with the channel, service, and method ID of 76 // the provided packet. ClientError(const Packet & packet,Status status)77 static constexpr Packet ClientError(const Packet& packet, Status status) { 78 return Packet(pwpb::PacketType::CLIENT_ERROR, 79 packet.channel_id(), 80 packet.service_id(), 81 packet.method_id(), 82 packet.call_id(), 83 {}, 84 status); 85 } 86 87 // Creates an empty packet. Packet()88 constexpr Packet() 89 : Packet(pwpb::PacketType{}, 90 kUnassignedId, 91 kUnassignedId, 92 kUnassignedId, 93 kUnassignedId) {} 94 95 constexpr Packet(pwpb::PacketType type, 96 uint32_t channel_id, 97 uint32_t service_id, 98 uint32_t method_id, 99 uint32_t call_id, 100 ConstByteSpan payload = {}, 101 Status status = OkStatus()) type_(type)102 : type_(type), 103 channel_id_(channel_id), 104 service_id_(service_id), 105 method_id_(method_id), 106 call_id_(call_id), 107 payload_(payload), 108 status_(status) {} 109 110 // Encodes the packet into its wire format. Returns the encoded size. 111 Result<ConstByteSpan> Encode(ByteSpan buffer) const; 112 113 // Determines the space required to encode the packet proto fields for a 114 // response, excluding the payload. This may be used to split the buffer into 115 // reserved space and available space for the payload. 116 // 117 // This method allocates two bytes for the status. Status code 0 (OK) is not 118 // encoded since 0 is the default value. 119 size_t MinEncodedSizeBytes() const; 120 121 enum Destination : bool { kServer, kClient }; 122 destination()123 constexpr Destination destination() const { 124 return static_cast<int>(type_) % 2 == 0 ? kServer : kClient; 125 } 126 type()127 constexpr pwpb::PacketType type() const { return type_; } channel_id()128 constexpr uint32_t channel_id() const { return channel_id_; } service_id()129 constexpr uint32_t service_id() const { return service_id_; } method_id()130 constexpr uint32_t method_id() const { return method_id_; } call_id()131 constexpr uint32_t call_id() const { return call_id_; } payload()132 constexpr const ConstByteSpan& payload() const { return payload_; } status()133 constexpr const Status& status() const { return status_; } 134 set_type(pwpb::PacketType type)135 constexpr void set_type(pwpb::PacketType type) { type_ = type; } set_channel_id(uint32_t channel_id)136 constexpr void set_channel_id(uint32_t channel_id) { 137 channel_id_ = channel_id; 138 } set_service_id(uint32_t service_id)139 constexpr void set_service_id(uint32_t service_id) { 140 service_id_ = service_id; 141 } set_method_id(uint32_t method_id)142 constexpr void set_method_id(uint32_t method_id) { method_id_ = method_id; } set_call_id(uint32_t call_id)143 constexpr void set_call_id(uint32_t call_id) { call_id_ = call_id; } set_payload(ConstByteSpan payload)144 constexpr void set_payload(ConstByteSpan payload) { payload_ = payload; } set_status(Status status)145 constexpr void set_status(Status status) { status_ = status; } 146 147 // Logs detailed info about this packet at INFO level. NOT for production use! 148 void DebugLog() const; 149 150 private: 151 pwpb::PacketType type_; 152 uint32_t channel_id_; 153 uint32_t service_id_; 154 uint32_t method_id_; 155 uint32_t call_id_; 156 ConstByteSpan payload_; 157 Status status_; 158 }; 159 160 } // namespace pw::rpc::internal 161