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 15 #include "pw_rpc/internal/packet.h" 16 17 #include "pw_protobuf/decoder.h" 18 19 namespace pw::rpc::internal { 20 21 using pwpb::PacketType; 22 23 namespace RpcPacket = pwpb::RpcPacket; 24 FromBuffer(ConstByteSpan data)25Result<Packet> Packet::FromBuffer(ConstByteSpan data) { 26 Packet packet; 27 Status status; 28 protobuf::Decoder decoder(data); 29 30 while ((status = decoder.Next()).ok()) { 31 RpcPacket::Fields field = 32 static_cast<RpcPacket::Fields>(decoder.FieldNumber()); 33 34 switch (field) { 35 case RpcPacket::Fields::kType: { 36 uint32_t value; 37 // A decode error will propagate from Next() and terminate the loop. 38 decoder.ReadUint32(&value).IgnoreError(); 39 packet.set_type(static_cast<PacketType>(value)); 40 break; 41 } 42 43 case RpcPacket::Fields::kChannelId: 44 // A decode error will propagate from Next() and terminate the loop. 45 decoder.ReadUint32(&packet.channel_id_).IgnoreError(); 46 break; 47 48 case RpcPacket::Fields::kServiceId: 49 // A decode error will propagate from Next() and terminate the loop. 50 decoder.ReadFixed32(&packet.service_id_).IgnoreError(); 51 break; 52 53 case RpcPacket::Fields::kMethodId: 54 // A decode error will propagate from Next() and terminate the loop. 55 decoder.ReadFixed32(&packet.method_id_).IgnoreError(); 56 break; 57 58 case RpcPacket::Fields::kPayload: 59 // A decode error will propagate from Next() and terminate the loop. 60 decoder.ReadBytes(&packet.payload_).IgnoreError(); 61 break; 62 63 case RpcPacket::Fields::kStatus: { 64 uint32_t value; 65 // A decode error will propagate from Next() and terminate the loop. 66 decoder.ReadUint32(&value).IgnoreError(); 67 packet.set_status(static_cast<Status::Code>(value)); 68 break; 69 } 70 71 case RpcPacket::Fields::kCallId: 72 // A decode error will propagate from Next() and terminate the loop. 73 decoder.ReadUint32(&packet.call_id_).IgnoreError(); 74 break; 75 } 76 } 77 78 if (status.IsDataLoss()) { 79 return status; 80 } 81 82 return packet; 83 } 84 Encode(ByteSpan buffer) const85Result<ConstByteSpan> Packet::Encode(ByteSpan buffer) const { 86 RpcPacket::MemoryEncoder rpc_packet(buffer); 87 88 // The payload is encoded first, as it may share the encode buffer. 89 if (!payload_.empty()) { 90 rpc_packet.WritePayload(payload_).IgnoreError(); 91 } 92 93 rpc_packet.WriteType(type_).IgnoreError(); 94 rpc_packet.WriteChannelId(channel_id_).IgnoreError(); 95 rpc_packet.WriteServiceId(service_id_).IgnoreError(); 96 rpc_packet.WriteMethodId(method_id_).IgnoreError(); 97 98 // Status code 0 is OK. In protobufs, 0 is the default int value, so skip 99 // encoding it to save two bytes in the output. 100 if (status_.code() != 0) { 101 rpc_packet.WriteStatus(status_.code()).IgnoreError(); 102 } 103 104 if (call_id_ != 0) { 105 rpc_packet.WriteCallId(call_id_).IgnoreError(); 106 } 107 108 if (rpc_packet.status().ok()) { 109 return ConstByteSpan(rpc_packet); 110 } 111 return rpc_packet.status(); 112 } 113 MinEncodedSizeBytes() const114size_t Packet::MinEncodedSizeBytes() const { 115 size_t reserved_size = 0; 116 117 reserved_size += 1; // channel_id key 118 reserved_size += varint::EncodedSize(channel_id()); 119 reserved_size += 1 + sizeof(uint32_t); // service_id key and fixed32 120 reserved_size += 1 + sizeof(uint32_t); // method_id key and fixed32 121 122 // Packet type always takes two bytes to encode (varint key + varint enum). 123 reserved_size += 2; 124 125 // Status field takes up to two bytes to encode (varint key + varint status). 126 reserved_size += 2; 127 128 // Payload field takes at least two bytes to encode (varint key + length). 129 reserved_size += 2; 130 131 return reserved_size; 132 } 133 134 } // namespace pw::rpc::internal 135