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