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