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( 90 pwpb::PacketType{}, kUnassignedId, kUnassignedId, kUnassignedId) {} 91 92 constexpr Packet(pwpb::PacketType type, 93 uint32_t channel_id, 94 uint32_t service_id, 95 uint32_t method_id, 96 uint32_t call_id = kUnassignedId, 97 ConstByteSpan payload = {}, 98 Status status = OkStatus()) type_(type)99 : type_(type), 100 channel_id_(channel_id), 101 service_id_(service_id), 102 method_id_(method_id), 103 call_id_(call_id), 104 payload_(payload), 105 status_(status) {} 106 107 // Encodes the packet into its wire format. Returns the encoded size. 108 Result<ConstByteSpan> Encode(ByteSpan buffer) const; 109 110 // Determines the space required to encode the packet proto fields for a 111 // response, excluding the payload. This may be used to split the buffer into 112 // reserved space and available space for the payload. 113 // 114 // This method allocates two bytes for the status. Status code 0 (OK) is not 115 // encoded since 0 is the default value. 116 size_t MinEncodedSizeBytes() const; 117 118 enum Destination : bool { kServer, kClient }; 119 destination()120 constexpr Destination destination() const { 121 return static_cast<int>(type_) % 2 == 0 ? kServer : kClient; 122 } 123 type()124 constexpr pwpb::PacketType type() const { return type_; } channel_id()125 constexpr uint32_t channel_id() const { return channel_id_; } service_id()126 constexpr uint32_t service_id() const { return service_id_; } method_id()127 constexpr uint32_t method_id() const { return method_id_; } call_id()128 constexpr uint32_t call_id() const { return call_id_; } payload()129 constexpr const ConstByteSpan& payload() const { return payload_; } status()130 constexpr const Status& status() const { return status_; } 131 set_type(pwpb::PacketType type)132 constexpr void set_type(pwpb::PacketType type) { type_ = type; } set_channel_id(uint32_t channel_id)133 constexpr void set_channel_id(uint32_t channel_id) { 134 channel_id_ = channel_id; 135 } set_service_id(uint32_t service_id)136 constexpr void set_service_id(uint32_t service_id) { 137 service_id_ = service_id; 138 } set_method_id(uint32_t method_id)139 constexpr void set_method_id(uint32_t method_id) { method_id_ = method_id; } set_call_id(uint32_t call_id)140 constexpr void set_call_id(uint32_t call_id) { call_id_ = call_id; } set_payload(ConstByteSpan payload)141 constexpr void set_payload(ConstByteSpan payload) { payload_ = payload; } set_status(Status status)142 constexpr void set_status(Status status) { status_ = status; } 143 144 private: 145 pwpb::PacketType type_; 146 uint32_t channel_id_; 147 uint32_t service_id_; 148 uint32_t method_id_; 149 uint32_t call_id_; 150 ConstByteSpan payload_; 151 Status status_; 152 }; 153 154 } // namespace pw::rpc::internal 155