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