xref: /aosp_15_r20/external/pigweed/pw_transfer/chunk.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1*61c4878aSAndroid Build Coastguard Worker // Copyright 2023 The Pigweed Authors
2*61c4878aSAndroid Build Coastguard Worker //
3*61c4878aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4*61c4878aSAndroid Build Coastguard Worker // use this file except in compliance with the License. You may obtain a copy of
5*61c4878aSAndroid Build Coastguard Worker // the License at
6*61c4878aSAndroid Build Coastguard Worker //
7*61c4878aSAndroid Build Coastguard Worker //     https://www.apache.org/licenses/LICENSE-2.0
8*61c4878aSAndroid Build Coastguard Worker //
9*61c4878aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*61c4878aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11*61c4878aSAndroid Build Coastguard Worker // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12*61c4878aSAndroid Build Coastguard Worker // License for the specific language governing permissions and limitations under
13*61c4878aSAndroid Build Coastguard Worker // the License.
14*61c4878aSAndroid Build Coastguard Worker 
15*61c4878aSAndroid Build Coastguard Worker #define PW_LOG_MODULE_NAME "TRN"
16*61c4878aSAndroid Build Coastguard Worker #define PW_LOG_LEVEL PW_TRANSFER_CONFIG_LOG_LEVEL
17*61c4878aSAndroid Build Coastguard Worker 
18*61c4878aSAndroid Build Coastguard Worker #include "pw_transfer/internal/chunk.h"
19*61c4878aSAndroid Build Coastguard Worker 
20*61c4878aSAndroid Build Coastguard Worker #include "pw_assert/check.h"
21*61c4878aSAndroid Build Coastguard Worker #include "pw_log/log.h"
22*61c4878aSAndroid Build Coastguard Worker #include "pw_log/rate_limited.h"
23*61c4878aSAndroid Build Coastguard Worker #include "pw_protobuf/decoder.h"
24*61c4878aSAndroid Build Coastguard Worker #include "pw_protobuf/serialized_size.h"
25*61c4878aSAndroid Build Coastguard Worker #include "pw_status/try.h"
26*61c4878aSAndroid Build Coastguard Worker #include "pw_transfer/internal/config.h"
27*61c4878aSAndroid Build Coastguard Worker 
28*61c4878aSAndroid Build Coastguard Worker namespace pw::transfer::internal {
29*61c4878aSAndroid Build Coastguard Worker 
30*61c4878aSAndroid Build Coastguard Worker namespace ProtoChunk = transfer::pwpb::Chunk;
31*61c4878aSAndroid Build Coastguard Worker 
ExtractIdentifier(ConstByteSpan message)32*61c4878aSAndroid Build Coastguard Worker Result<Chunk::Identifier> Chunk::ExtractIdentifier(ConstByteSpan message) {
33*61c4878aSAndroid Build Coastguard Worker   protobuf::Decoder decoder(message);
34*61c4878aSAndroid Build Coastguard Worker 
35*61c4878aSAndroid Build Coastguard Worker   uint32_t session_id = 0;
36*61c4878aSAndroid Build Coastguard Worker   uint32_t desired_session_id = 0;
37*61c4878aSAndroid Build Coastguard Worker   bool legacy = true;
38*61c4878aSAndroid Build Coastguard Worker 
39*61c4878aSAndroid Build Coastguard Worker   Status status;
40*61c4878aSAndroid Build Coastguard Worker 
41*61c4878aSAndroid Build Coastguard Worker   while ((status = decoder.Next()).ok()) {
42*61c4878aSAndroid Build Coastguard Worker     ProtoChunk::Fields field =
43*61c4878aSAndroid Build Coastguard Worker         static_cast<ProtoChunk::Fields>(decoder.FieldNumber());
44*61c4878aSAndroid Build Coastguard Worker 
45*61c4878aSAndroid Build Coastguard Worker     if (field == ProtoChunk::Fields::kTransferId) {
46*61c4878aSAndroid Build Coastguard Worker       // Interpret a legacy transfer_id field as a session ID if an explicit
47*61c4878aSAndroid Build Coastguard Worker       // session_id field has not already been seen.
48*61c4878aSAndroid Build Coastguard Worker       if (session_id == 0) {
49*61c4878aSAndroid Build Coastguard Worker         PW_TRY(decoder.ReadUint32(&session_id));
50*61c4878aSAndroid Build Coastguard Worker       }
51*61c4878aSAndroid Build Coastguard Worker     } else if (field == ProtoChunk::Fields::kSessionId) {
52*61c4878aSAndroid Build Coastguard Worker       // A session_id field always takes precedence over transfer_id.
53*61c4878aSAndroid Build Coastguard Worker       PW_TRY(decoder.ReadUint32(&session_id));
54*61c4878aSAndroid Build Coastguard Worker       legacy = false;
55*61c4878aSAndroid Build Coastguard Worker     } else if (field == ProtoChunk::Fields::kDesiredSessionId) {
56*61c4878aSAndroid Build Coastguard Worker       PW_TRY(decoder.ReadUint32(&desired_session_id));
57*61c4878aSAndroid Build Coastguard Worker     }
58*61c4878aSAndroid Build Coastguard Worker   }
59*61c4878aSAndroid Build Coastguard Worker 
60*61c4878aSAndroid Build Coastguard Worker   if (!status.IsOutOfRange()) {
61*61c4878aSAndroid Build Coastguard Worker     return Status::DataLoss();
62*61c4878aSAndroid Build Coastguard Worker   }
63*61c4878aSAndroid Build Coastguard Worker 
64*61c4878aSAndroid Build Coastguard Worker   if (desired_session_id != 0) {
65*61c4878aSAndroid Build Coastguard Worker     // Can't have both a desired and regular session_id.
66*61c4878aSAndroid Build Coastguard Worker     if (!legacy && session_id != 0) {
67*61c4878aSAndroid Build Coastguard Worker       return Status::DataLoss();
68*61c4878aSAndroid Build Coastguard Worker     }
69*61c4878aSAndroid Build Coastguard Worker     return Identifier::Desired(desired_session_id);
70*61c4878aSAndroid Build Coastguard Worker   }
71*61c4878aSAndroid Build Coastguard Worker 
72*61c4878aSAndroid Build Coastguard Worker   if (session_id != 0) {
73*61c4878aSAndroid Build Coastguard Worker     return legacy ? Identifier::Legacy(session_id)
74*61c4878aSAndroid Build Coastguard Worker                   : Identifier::Session(session_id);
75*61c4878aSAndroid Build Coastguard Worker   }
76*61c4878aSAndroid Build Coastguard Worker 
77*61c4878aSAndroid Build Coastguard Worker   return Status::DataLoss();
78*61c4878aSAndroid Build Coastguard Worker }
79*61c4878aSAndroid Build Coastguard Worker 
Parse(ConstByteSpan message)80*61c4878aSAndroid Build Coastguard Worker Result<Chunk> Chunk::Parse(ConstByteSpan message) {
81*61c4878aSAndroid Build Coastguard Worker   protobuf::Decoder decoder(message);
82*61c4878aSAndroid Build Coastguard Worker   Status status;
83*61c4878aSAndroid Build Coastguard Worker   uint32_t value;
84*61c4878aSAndroid Build Coastguard Worker 
85*61c4878aSAndroid Build Coastguard Worker   Chunk chunk;
86*61c4878aSAndroid Build Coastguard Worker 
87*61c4878aSAndroid Build Coastguard Worker   // Determine the protocol version of the chunk depending on field presence in
88*61c4878aSAndroid Build Coastguard Worker   // the serialized message.
89*61c4878aSAndroid Build Coastguard Worker   chunk.protocol_version_ = ProtocolVersion::kUnknown;
90*61c4878aSAndroid Build Coastguard Worker 
91*61c4878aSAndroid Build Coastguard Worker   // Some older versions of the protocol set the deprecated pending_bytes field
92*61c4878aSAndroid Build Coastguard Worker   // in their chunks. The newer transfer handling code does not process this
93*61c4878aSAndroid Build Coastguard Worker   // field, instead working only in terms of window_end_offset. If pending_bytes
94*61c4878aSAndroid Build Coastguard Worker   // is encountered in the serialized message, save its value, then calculate
95*61c4878aSAndroid Build Coastguard Worker   // window_end_offset from it once parsing is complete.
96*61c4878aSAndroid Build Coastguard Worker   uint32_t pending_bytes = 0;
97*61c4878aSAndroid Build Coastguard Worker 
98*61c4878aSAndroid Build Coastguard Worker   bool has_session_id = false;
99*61c4878aSAndroid Build Coastguard Worker 
100*61c4878aSAndroid Build Coastguard Worker   while ((status = decoder.Next()).ok()) {
101*61c4878aSAndroid Build Coastguard Worker     ProtoChunk::Fields field =
102*61c4878aSAndroid Build Coastguard Worker         static_cast<ProtoChunk::Fields>(decoder.FieldNumber());
103*61c4878aSAndroid Build Coastguard Worker 
104*61c4878aSAndroid Build Coastguard Worker     switch (field) {
105*61c4878aSAndroid Build Coastguard Worker       case ProtoChunk::Fields::kTransferId:
106*61c4878aSAndroid Build Coastguard Worker         // transfer_id is a legacy field. session_id will always take precedence
107*61c4878aSAndroid Build Coastguard Worker         // over it, so it should only be read if session_id has not yet been
108*61c4878aSAndroid Build Coastguard Worker         // encountered.
109*61c4878aSAndroid Build Coastguard Worker         if (chunk.session_id_ == 0) {
110*61c4878aSAndroid Build Coastguard Worker           PW_TRY(decoder.ReadUint32(&chunk.session_id_));
111*61c4878aSAndroid Build Coastguard Worker         }
112*61c4878aSAndroid Build Coastguard Worker         break;
113*61c4878aSAndroid Build Coastguard Worker 
114*61c4878aSAndroid Build Coastguard Worker       case ProtoChunk::Fields::kSessionId:
115*61c4878aSAndroid Build Coastguard Worker         // The existence of a session_id field indicates that a newer protocol
116*61c4878aSAndroid Build Coastguard Worker         // is running. Update the deduced protocol unless it was explicitly
117*61c4878aSAndroid Build Coastguard Worker         // specified.
118*61c4878aSAndroid Build Coastguard Worker         if (chunk.protocol_version_ == ProtocolVersion::kUnknown) {
119*61c4878aSAndroid Build Coastguard Worker           chunk.protocol_version_ = ProtocolVersion::kVersionTwo;
120*61c4878aSAndroid Build Coastguard Worker         }
121*61c4878aSAndroid Build Coastguard Worker         has_session_id = true;
122*61c4878aSAndroid Build Coastguard Worker         PW_TRY(decoder.ReadUint32(&chunk.session_id_));
123*61c4878aSAndroid Build Coastguard Worker         break;
124*61c4878aSAndroid Build Coastguard Worker 
125*61c4878aSAndroid Build Coastguard Worker       case ProtoChunk::Fields::kPendingBytes:
126*61c4878aSAndroid Build Coastguard Worker         PW_TRY(decoder.ReadUint32(&pending_bytes));
127*61c4878aSAndroid Build Coastguard Worker         break;
128*61c4878aSAndroid Build Coastguard Worker 
129*61c4878aSAndroid Build Coastguard Worker       case ProtoChunk::Fields::kMaxChunkSizeBytes:
130*61c4878aSAndroid Build Coastguard Worker         PW_TRY(decoder.ReadUint32(&value));
131*61c4878aSAndroid Build Coastguard Worker         chunk.set_max_chunk_size_bytes(value);
132*61c4878aSAndroid Build Coastguard Worker         break;
133*61c4878aSAndroid Build Coastguard Worker 
134*61c4878aSAndroid Build Coastguard Worker       case ProtoChunk::Fields::kMinDelayMicroseconds:
135*61c4878aSAndroid Build Coastguard Worker         PW_TRY(decoder.ReadUint32(&value));
136*61c4878aSAndroid Build Coastguard Worker         chunk.set_min_delay_microseconds(value);
137*61c4878aSAndroid Build Coastguard Worker         break;
138*61c4878aSAndroid Build Coastguard Worker 
139*61c4878aSAndroid Build Coastguard Worker       case ProtoChunk::Fields::kOffset:
140*61c4878aSAndroid Build Coastguard Worker         PW_TRY(decoder.ReadUint32(&chunk.offset_));
141*61c4878aSAndroid Build Coastguard Worker         break;
142*61c4878aSAndroid Build Coastguard Worker 
143*61c4878aSAndroid Build Coastguard Worker       case ProtoChunk::Fields::kData:
144*61c4878aSAndroid Build Coastguard Worker         PW_TRY(decoder.ReadBytes(&chunk.payload_));
145*61c4878aSAndroid Build Coastguard Worker         break;
146*61c4878aSAndroid Build Coastguard Worker 
147*61c4878aSAndroid Build Coastguard Worker       case ProtoChunk::Fields::kRemainingBytes: {
148*61c4878aSAndroid Build Coastguard Worker         uint64_t remaining_bytes;
149*61c4878aSAndroid Build Coastguard Worker         PW_TRY(decoder.ReadUint64(&remaining_bytes));
150*61c4878aSAndroid Build Coastguard Worker         chunk.set_remaining_bytes(remaining_bytes);
151*61c4878aSAndroid Build Coastguard Worker         break;
152*61c4878aSAndroid Build Coastguard Worker       }
153*61c4878aSAndroid Build Coastguard Worker 
154*61c4878aSAndroid Build Coastguard Worker       case ProtoChunk::Fields::kStatus:
155*61c4878aSAndroid Build Coastguard Worker         PW_TRY(decoder.ReadUint32(&value));
156*61c4878aSAndroid Build Coastguard Worker         chunk.set_status(static_cast<Status::Code>(value));
157*61c4878aSAndroid Build Coastguard Worker         break;
158*61c4878aSAndroid Build Coastguard Worker 
159*61c4878aSAndroid Build Coastguard Worker       case ProtoChunk::Fields::kWindowEndOffset:
160*61c4878aSAndroid Build Coastguard Worker         PW_TRY(decoder.ReadUint32(&chunk.window_end_offset_));
161*61c4878aSAndroid Build Coastguard Worker         break;
162*61c4878aSAndroid Build Coastguard Worker 
163*61c4878aSAndroid Build Coastguard Worker       case ProtoChunk::Fields::kType: {
164*61c4878aSAndroid Build Coastguard Worker         uint32_t type;
165*61c4878aSAndroid Build Coastguard Worker         PW_TRY(decoder.ReadUint32(&type));
166*61c4878aSAndroid Build Coastguard Worker         chunk.type_ = static_cast<Chunk::Type>(type);
167*61c4878aSAndroid Build Coastguard Worker         break;
168*61c4878aSAndroid Build Coastguard Worker       }
169*61c4878aSAndroid Build Coastguard Worker 
170*61c4878aSAndroid Build Coastguard Worker       case ProtoChunk::Fields::kResourceId:
171*61c4878aSAndroid Build Coastguard Worker         PW_TRY(decoder.ReadUint32(&value));
172*61c4878aSAndroid Build Coastguard Worker         chunk.set_resource_id(value);
173*61c4878aSAndroid Build Coastguard Worker         break;
174*61c4878aSAndroid Build Coastguard Worker 
175*61c4878aSAndroid Build Coastguard Worker       case ProtoChunk::Fields::kProtocolVersion:
176*61c4878aSAndroid Build Coastguard Worker         // The protocol_version field is added as part of the initial handshake
177*61c4878aSAndroid Build Coastguard Worker         // starting from version 2. If provided, it should override any deduced
178*61c4878aSAndroid Build Coastguard Worker         // protocol version.
179*61c4878aSAndroid Build Coastguard Worker         PW_TRY(decoder.ReadUint32(&value));
180*61c4878aSAndroid Build Coastguard Worker         if (!ValidProtocolVersion(value)) {
181*61c4878aSAndroid Build Coastguard Worker           return Status::DataLoss();
182*61c4878aSAndroid Build Coastguard Worker         }
183*61c4878aSAndroid Build Coastguard Worker         chunk.protocol_version_ = static_cast<ProtocolVersion>(value);
184*61c4878aSAndroid Build Coastguard Worker         break;
185*61c4878aSAndroid Build Coastguard Worker 
186*61c4878aSAndroid Build Coastguard Worker       case ProtoChunk::Fields::kDesiredSessionId:
187*61c4878aSAndroid Build Coastguard Worker         PW_TRY(decoder.ReadUint32(&value));
188*61c4878aSAndroid Build Coastguard Worker         chunk.desired_session_id_ = value;
189*61c4878aSAndroid Build Coastguard Worker         break;
190*61c4878aSAndroid Build Coastguard Worker 
191*61c4878aSAndroid Build Coastguard Worker       case ProtoChunk::Fields::kInitialOffset:
192*61c4878aSAndroid Build Coastguard Worker         PW_TRY(decoder.ReadUint32(&value));
193*61c4878aSAndroid Build Coastguard Worker         chunk.set_initial_offset(value);
194*61c4878aSAndroid Build Coastguard Worker         break;
195*61c4878aSAndroid Build Coastguard Worker 
196*61c4878aSAndroid Build Coastguard Worker         // Silently ignore any unrecognized fields.
197*61c4878aSAndroid Build Coastguard Worker     }
198*61c4878aSAndroid Build Coastguard Worker   }
199*61c4878aSAndroid Build Coastguard Worker 
200*61c4878aSAndroid Build Coastguard Worker   if (chunk.desired_session_id_.has_value() && has_session_id) {
201*61c4878aSAndroid Build Coastguard Worker     // Setting both session_id and desired_session_id is not permitted.
202*61c4878aSAndroid Build Coastguard Worker     return Status::DataLoss();
203*61c4878aSAndroid Build Coastguard Worker   }
204*61c4878aSAndroid Build Coastguard Worker 
205*61c4878aSAndroid Build Coastguard Worker   if (chunk.protocol_version_ == ProtocolVersion::kUnknown) {
206*61c4878aSAndroid Build Coastguard Worker     // If no fields in the chunk specified its protocol version, assume it is a
207*61c4878aSAndroid Build Coastguard Worker     // legacy chunk.
208*61c4878aSAndroid Build Coastguard Worker     chunk.protocol_version_ = ProtocolVersion::kLegacy;
209*61c4878aSAndroid Build Coastguard Worker   }
210*61c4878aSAndroid Build Coastguard Worker 
211*61c4878aSAndroid Build Coastguard Worker   if (pending_bytes != 0) {
212*61c4878aSAndroid Build Coastguard Worker     // Compute window_end_offset if it isn't explicitly provided (in older
213*61c4878aSAndroid Build Coastguard Worker     // protocol versions).
214*61c4878aSAndroid Build Coastguard Worker     chunk.set_window_end_offset(chunk.offset() + pending_bytes);
215*61c4878aSAndroid Build Coastguard Worker   }
216*61c4878aSAndroid Build Coastguard Worker 
217*61c4878aSAndroid Build Coastguard Worker   if (status.ok() || status.IsOutOfRange()) {
218*61c4878aSAndroid Build Coastguard Worker     return chunk;
219*61c4878aSAndroid Build Coastguard Worker   }
220*61c4878aSAndroid Build Coastguard Worker 
221*61c4878aSAndroid Build Coastguard Worker   return status;
222*61c4878aSAndroid Build Coastguard Worker }
223*61c4878aSAndroid Build Coastguard Worker 
Encode(ByteSpan buffer) const224*61c4878aSAndroid Build Coastguard Worker Result<ConstByteSpan> Chunk::Encode(ByteSpan buffer) const {
225*61c4878aSAndroid Build Coastguard Worker   PW_CHECK(protocol_version_ != ProtocolVersion::kUnknown,
226*61c4878aSAndroid Build Coastguard Worker            "Cannot encode a transfer chunk with an unknown protocol version");
227*61c4878aSAndroid Build Coastguard Worker 
228*61c4878aSAndroid Build Coastguard Worker   ProtoChunk::MemoryEncoder encoder(buffer);
229*61c4878aSAndroid Build Coastguard Worker 
230*61c4878aSAndroid Build Coastguard Worker   // Write the payload first to avoid clobbering it if it shares the same buffer
231*61c4878aSAndroid Build Coastguard Worker   // as the encode buffer.
232*61c4878aSAndroid Build Coastguard Worker   if (has_payload()) {
233*61c4878aSAndroid Build Coastguard Worker     encoder.WriteData(payload_).IgnoreError();
234*61c4878aSAndroid Build Coastguard Worker   }
235*61c4878aSAndroid Build Coastguard Worker 
236*61c4878aSAndroid Build Coastguard Worker   if (protocol_version_ >= ProtocolVersion::kVersionTwo) {
237*61c4878aSAndroid Build Coastguard Worker     if (session_id_ != 0) {
238*61c4878aSAndroid Build Coastguard Worker       PW_CHECK(!desired_session_id_.has_value(),
239*61c4878aSAndroid Build Coastguard Worker                "A chunk cannot set both a desired and regular session ID");
240*61c4878aSAndroid Build Coastguard Worker       encoder.WriteSessionId(session_id_).IgnoreError();
241*61c4878aSAndroid Build Coastguard Worker     }
242*61c4878aSAndroid Build Coastguard Worker 
243*61c4878aSAndroid Build Coastguard Worker     if (desired_session_id_.has_value()) {
244*61c4878aSAndroid Build Coastguard Worker       encoder.WriteDesiredSessionId(desired_session_id_.value()).IgnoreError();
245*61c4878aSAndroid Build Coastguard Worker     }
246*61c4878aSAndroid Build Coastguard Worker 
247*61c4878aSAndroid Build Coastguard Worker     if (resource_id_.has_value()) {
248*61c4878aSAndroid Build Coastguard Worker       encoder.WriteResourceId(resource_id_.value()).IgnoreError();
249*61c4878aSAndroid Build Coastguard Worker     }
250*61c4878aSAndroid Build Coastguard Worker   }
251*61c4878aSAndroid Build Coastguard Worker 
252*61c4878aSAndroid Build Coastguard Worker   // During the initial handshake, the chunk's configured protocol version is
253*61c4878aSAndroid Build Coastguard Worker   // explicitly serialized to the wire.
254*61c4878aSAndroid Build Coastguard Worker   if (IsInitialHandshakeChunk()) {
255*61c4878aSAndroid Build Coastguard Worker     encoder.WriteProtocolVersion(static_cast<uint32_t>(protocol_version_))
256*61c4878aSAndroid Build Coastguard Worker         .IgnoreError();
257*61c4878aSAndroid Build Coastguard Worker   }
258*61c4878aSAndroid Build Coastguard Worker 
259*61c4878aSAndroid Build Coastguard Worker   if (type_.has_value()) {
260*61c4878aSAndroid Build Coastguard Worker     encoder.WriteType(static_cast<ProtoChunk::Type>(type_.value()))
261*61c4878aSAndroid Build Coastguard Worker         .IgnoreError();
262*61c4878aSAndroid Build Coastguard Worker   }
263*61c4878aSAndroid Build Coastguard Worker 
264*61c4878aSAndroid Build Coastguard Worker   if (window_end_offset_ != 0) {
265*61c4878aSAndroid Build Coastguard Worker     encoder.WriteWindowEndOffset(window_end_offset_).IgnoreError();
266*61c4878aSAndroid Build Coastguard Worker   }
267*61c4878aSAndroid Build Coastguard Worker 
268*61c4878aSAndroid Build Coastguard Worker   // Encode additional fields from the legacy protocol.
269*61c4878aSAndroid Build Coastguard Worker   if (ShouldEncodeLegacyFields()) {
270*61c4878aSAndroid Build Coastguard Worker     // The legacy protocol uses the transfer_id field instead of session_id or
271*61c4878aSAndroid Build Coastguard Worker     // resource_id.
272*61c4878aSAndroid Build Coastguard Worker     if (resource_id_.has_value()) {
273*61c4878aSAndroid Build Coastguard Worker       encoder.WriteTransferId(resource_id_.value()).IgnoreError();
274*61c4878aSAndroid Build Coastguard Worker     } else {
275*61c4878aSAndroid Build Coastguard Worker       encoder.WriteTransferId(session_id_).IgnoreError();
276*61c4878aSAndroid Build Coastguard Worker     }
277*61c4878aSAndroid Build Coastguard Worker 
278*61c4878aSAndroid Build Coastguard Worker     // In the legacy protocol, the pending_bytes field must be set alongside
279*61c4878aSAndroid Build Coastguard Worker     // window_end_offset, as some transfer implementations require it.
280*61c4878aSAndroid Build Coastguard Worker     if (window_end_offset_ != 0) {
281*61c4878aSAndroid Build Coastguard Worker       encoder.WritePendingBytes(window_end_offset_ - offset_).IgnoreError();
282*61c4878aSAndroid Build Coastguard Worker     }
283*61c4878aSAndroid Build Coastguard Worker   }
284*61c4878aSAndroid Build Coastguard Worker 
285*61c4878aSAndroid Build Coastguard Worker   if (max_chunk_size_bytes_.has_value()) {
286*61c4878aSAndroid Build Coastguard Worker     encoder.WriteMaxChunkSizeBytes(max_chunk_size_bytes_.value()).IgnoreError();
287*61c4878aSAndroid Build Coastguard Worker   }
288*61c4878aSAndroid Build Coastguard Worker   if (min_delay_microseconds_.has_value()) {
289*61c4878aSAndroid Build Coastguard Worker     encoder.WriteMinDelayMicroseconds(min_delay_microseconds_.value())
290*61c4878aSAndroid Build Coastguard Worker         .IgnoreError();
291*61c4878aSAndroid Build Coastguard Worker   }
292*61c4878aSAndroid Build Coastguard Worker 
293*61c4878aSAndroid Build Coastguard Worker   if (offset_ != 0) {
294*61c4878aSAndroid Build Coastguard Worker     encoder.WriteOffset(offset_).IgnoreError();
295*61c4878aSAndroid Build Coastguard Worker   }
296*61c4878aSAndroid Build Coastguard Worker 
297*61c4878aSAndroid Build Coastguard Worker   if (initial_offset_ != 0) {
298*61c4878aSAndroid Build Coastguard Worker     encoder.WriteInitialOffset(initial_offset_).IgnoreError();
299*61c4878aSAndroid Build Coastguard Worker   }
300*61c4878aSAndroid Build Coastguard Worker 
301*61c4878aSAndroid Build Coastguard Worker   if (remaining_bytes_.has_value()) {
302*61c4878aSAndroid Build Coastguard Worker     encoder.WriteRemainingBytes(remaining_bytes_.value()).IgnoreError();
303*61c4878aSAndroid Build Coastguard Worker   }
304*61c4878aSAndroid Build Coastguard Worker 
305*61c4878aSAndroid Build Coastguard Worker   if (status_.has_value()) {
306*61c4878aSAndroid Build Coastguard Worker     encoder.WriteStatus(status_.value().code()).IgnoreError();
307*61c4878aSAndroid Build Coastguard Worker   }
308*61c4878aSAndroid Build Coastguard Worker 
309*61c4878aSAndroid Build Coastguard Worker   PW_TRY(encoder.status());
310*61c4878aSAndroid Build Coastguard Worker   return ConstByteSpan(encoder);
311*61c4878aSAndroid Build Coastguard Worker }
312*61c4878aSAndroid Build Coastguard Worker 
EncodedSize() const313*61c4878aSAndroid Build Coastguard Worker size_t Chunk::EncodedSize() const {
314*61c4878aSAndroid Build Coastguard Worker   size_t size = 0;
315*61c4878aSAndroid Build Coastguard Worker 
316*61c4878aSAndroid Build Coastguard Worker   if (session_id_ != 0) {
317*61c4878aSAndroid Build Coastguard Worker     if (protocol_version_ >= ProtocolVersion::kVersionTwo) {
318*61c4878aSAndroid Build Coastguard Worker       size += protobuf::SizeOfVarintField(ProtoChunk::Fields::kSessionId,
319*61c4878aSAndroid Build Coastguard Worker                                           session_id_);
320*61c4878aSAndroid Build Coastguard Worker     }
321*61c4878aSAndroid Build Coastguard Worker 
322*61c4878aSAndroid Build Coastguard Worker     if (ShouldEncodeLegacyFields()) {
323*61c4878aSAndroid Build Coastguard Worker       if (resource_id_.has_value()) {
324*61c4878aSAndroid Build Coastguard Worker         size += protobuf::SizeOfVarintField(ProtoChunk::Fields::kTransferId,
325*61c4878aSAndroid Build Coastguard Worker                                             resource_id_.value());
326*61c4878aSAndroid Build Coastguard Worker       } else {
327*61c4878aSAndroid Build Coastguard Worker         size += protobuf::SizeOfVarintField(ProtoChunk::Fields::kTransferId,
328*61c4878aSAndroid Build Coastguard Worker                                             session_id_);
329*61c4878aSAndroid Build Coastguard Worker       }
330*61c4878aSAndroid Build Coastguard Worker     }
331*61c4878aSAndroid Build Coastguard Worker   }
332*61c4878aSAndroid Build Coastguard Worker 
333*61c4878aSAndroid Build Coastguard Worker   if (IsInitialHandshakeChunk()) {
334*61c4878aSAndroid Build Coastguard Worker     size +=
335*61c4878aSAndroid Build Coastguard Worker         protobuf::SizeOfVarintField(ProtoChunk::Fields::kProtocolVersion,
336*61c4878aSAndroid Build Coastguard Worker                                     static_cast<uint32_t>(protocol_version_));
337*61c4878aSAndroid Build Coastguard Worker   }
338*61c4878aSAndroid Build Coastguard Worker 
339*61c4878aSAndroid Build Coastguard Worker   if (protocol_version_ >= ProtocolVersion::kVersionTwo) {
340*61c4878aSAndroid Build Coastguard Worker     if (resource_id_.has_value()) {
341*61c4878aSAndroid Build Coastguard Worker       size += protobuf::SizeOfVarintField(ProtoChunk::Fields::kResourceId,
342*61c4878aSAndroid Build Coastguard Worker                                           resource_id_.value());
343*61c4878aSAndroid Build Coastguard Worker     }
344*61c4878aSAndroid Build Coastguard Worker     if (desired_session_id_.has_value()) {
345*61c4878aSAndroid Build Coastguard Worker       size += protobuf::SizeOfVarintField(ProtoChunk::Fields::kDesiredSessionId,
346*61c4878aSAndroid Build Coastguard Worker                                           desired_session_id_.value());
347*61c4878aSAndroid Build Coastguard Worker     }
348*61c4878aSAndroid Build Coastguard Worker   }
349*61c4878aSAndroid Build Coastguard Worker 
350*61c4878aSAndroid Build Coastguard Worker   if (offset_ != 0) {
351*61c4878aSAndroid Build Coastguard Worker     size += protobuf::SizeOfVarintField(ProtoChunk::Fields::kOffset, offset_);
352*61c4878aSAndroid Build Coastguard Worker   }
353*61c4878aSAndroid Build Coastguard Worker 
354*61c4878aSAndroid Build Coastguard Worker   if (window_end_offset_ != 0) {
355*61c4878aSAndroid Build Coastguard Worker     size += protobuf::SizeOfVarintField(ProtoChunk::Fields::kWindowEndOffset,
356*61c4878aSAndroid Build Coastguard Worker                                         window_end_offset_);
357*61c4878aSAndroid Build Coastguard Worker 
358*61c4878aSAndroid Build Coastguard Worker     if (ShouldEncodeLegacyFields()) {
359*61c4878aSAndroid Build Coastguard Worker       size += protobuf::SizeOfVarintField(ProtoChunk::Fields::kPendingBytes,
360*61c4878aSAndroid Build Coastguard Worker                                           window_end_offset_ - offset_);
361*61c4878aSAndroid Build Coastguard Worker     }
362*61c4878aSAndroid Build Coastguard Worker   }
363*61c4878aSAndroid Build Coastguard Worker 
364*61c4878aSAndroid Build Coastguard Worker   if (type_.has_value()) {
365*61c4878aSAndroid Build Coastguard Worker     size += protobuf::SizeOfVarintField(ProtoChunk::Fields::kType,
366*61c4878aSAndroid Build Coastguard Worker                                         static_cast<uint32_t>(type_.value()));
367*61c4878aSAndroid Build Coastguard Worker   }
368*61c4878aSAndroid Build Coastguard Worker 
369*61c4878aSAndroid Build Coastguard Worker   if (has_payload()) {
370*61c4878aSAndroid Build Coastguard Worker     size += protobuf::SizeOfDelimitedField(ProtoChunk::Fields::kData,
371*61c4878aSAndroid Build Coastguard Worker                                            payload_.size());
372*61c4878aSAndroid Build Coastguard Worker   }
373*61c4878aSAndroid Build Coastguard Worker 
374*61c4878aSAndroid Build Coastguard Worker   if (max_chunk_size_bytes_.has_value()) {
375*61c4878aSAndroid Build Coastguard Worker     size += protobuf::SizeOfVarintField(ProtoChunk::Fields::kMaxChunkSizeBytes,
376*61c4878aSAndroid Build Coastguard Worker                                         max_chunk_size_bytes_.value());
377*61c4878aSAndroid Build Coastguard Worker   }
378*61c4878aSAndroid Build Coastguard Worker 
379*61c4878aSAndroid Build Coastguard Worker   if (min_delay_microseconds_.has_value()) {
380*61c4878aSAndroid Build Coastguard Worker     size +=
381*61c4878aSAndroid Build Coastguard Worker         protobuf::SizeOfVarintField(ProtoChunk::Fields::kMinDelayMicroseconds,
382*61c4878aSAndroid Build Coastguard Worker                                     min_delay_microseconds_.value());
383*61c4878aSAndroid Build Coastguard Worker   }
384*61c4878aSAndroid Build Coastguard Worker 
385*61c4878aSAndroid Build Coastguard Worker   if (remaining_bytes_.has_value()) {
386*61c4878aSAndroid Build Coastguard Worker     size += protobuf::SizeOfVarintField(ProtoChunk::Fields::kRemainingBytes,
387*61c4878aSAndroid Build Coastguard Worker                                         remaining_bytes_.value());
388*61c4878aSAndroid Build Coastguard Worker   }
389*61c4878aSAndroid Build Coastguard Worker 
390*61c4878aSAndroid Build Coastguard Worker   if (status_.has_value()) {
391*61c4878aSAndroid Build Coastguard Worker     size += protobuf::SizeOfVarintField(ProtoChunk::Fields::kStatus,
392*61c4878aSAndroid Build Coastguard Worker                                         status_.value().code());
393*61c4878aSAndroid Build Coastguard Worker   }
394*61c4878aSAndroid Build Coastguard Worker 
395*61c4878aSAndroid Build Coastguard Worker   return size;
396*61c4878aSAndroid Build Coastguard Worker }
397*61c4878aSAndroid Build Coastguard Worker 
LogChunk(bool received,pw::chrono::SystemClock::duration rate_limit) const398*61c4878aSAndroid Build Coastguard Worker void Chunk::LogChunk(bool received,
399*61c4878aSAndroid Build Coastguard Worker                      pw::chrono::SystemClock::duration rate_limit) const {
400*61c4878aSAndroid Build Coastguard Worker   // Log in two different spots so the rate limiting applies separately to sent
401*61c4878aSAndroid Build Coastguard Worker   // and received
402*61c4878aSAndroid Build Coastguard Worker   if (received) {
403*61c4878aSAndroid Build Coastguard Worker     PW_LOG_EVERY_N_DURATION(
404*61c4878aSAndroid Build Coastguard Worker         PW_LOG_LEVEL_DEBUG,
405*61c4878aSAndroid Build Coastguard Worker         rate_limit,
406*61c4878aSAndroid Build Coastguard Worker         "Chunk received, type: %u, session id: %u, protocol version: %u,\n"
407*61c4878aSAndroid Build Coastguard Worker         "resource id: %d, desired session id: %d, offset: %u, size: %u,\n"
408*61c4878aSAndroid Build Coastguard Worker         "window end offset: %u, remaining bytes: %d, status: %d",
409*61c4878aSAndroid Build Coastguard Worker         type_.has_value() ? static_cast<unsigned>(type_.value()) : 0,
410*61c4878aSAndroid Build Coastguard Worker         static_cast<unsigned>(session_id_),
411*61c4878aSAndroid Build Coastguard Worker         static_cast<unsigned>(protocol_version_),
412*61c4878aSAndroid Build Coastguard Worker         resource_id_.has_value() ? static_cast<unsigned>(resource_id_.value())
413*61c4878aSAndroid Build Coastguard Worker                                  : -1,
414*61c4878aSAndroid Build Coastguard Worker         desired_session_id_.has_value()
415*61c4878aSAndroid Build Coastguard Worker             ? static_cast<int>(desired_session_id_.value())
416*61c4878aSAndroid Build Coastguard Worker             : -1,
417*61c4878aSAndroid Build Coastguard Worker         static_cast<unsigned>(offset_),
418*61c4878aSAndroid Build Coastguard Worker         has_payload() ? static_cast<unsigned>(payload_.size()) : 0,
419*61c4878aSAndroid Build Coastguard Worker         static_cast<unsigned>(window_end_offset_),
420*61c4878aSAndroid Build Coastguard Worker         remaining_bytes_.has_value()
421*61c4878aSAndroid Build Coastguard Worker             ? static_cast<unsigned>(remaining_bytes_.value())
422*61c4878aSAndroid Build Coastguard Worker             : -1,
423*61c4878aSAndroid Build Coastguard Worker         status_.has_value() ? static_cast<unsigned>(status_.value().code())
424*61c4878aSAndroid Build Coastguard Worker                             : -1);
425*61c4878aSAndroid Build Coastguard Worker   } else {
426*61c4878aSAndroid Build Coastguard Worker     PW_LOG_EVERY_N_DURATION(
427*61c4878aSAndroid Build Coastguard Worker         PW_LOG_LEVEL_DEBUG,
428*61c4878aSAndroid Build Coastguard Worker         rate_limit,
429*61c4878aSAndroid Build Coastguard Worker         "Chunk sent, type: %u, session id: %u, protocol version: %u,\n"
430*61c4878aSAndroid Build Coastguard Worker         "resource id: %d, desired session id: %d, offset: %u, size: %u,\n"
431*61c4878aSAndroid Build Coastguard Worker         "window end offset: %u, remaining bytes: %d, status: %d",
432*61c4878aSAndroid Build Coastguard Worker         type_.has_value() ? static_cast<unsigned>(type_.value()) : 0,
433*61c4878aSAndroid Build Coastguard Worker         static_cast<unsigned>(session_id_),
434*61c4878aSAndroid Build Coastguard Worker         static_cast<unsigned>(protocol_version_),
435*61c4878aSAndroid Build Coastguard Worker         resource_id_.has_value() ? static_cast<unsigned>(resource_id_.value())
436*61c4878aSAndroid Build Coastguard Worker                                  : -1,
437*61c4878aSAndroid Build Coastguard Worker         desired_session_id_.has_value()
438*61c4878aSAndroid Build Coastguard Worker             ? static_cast<int>(desired_session_id_.value())
439*61c4878aSAndroid Build Coastguard Worker             : -1,
440*61c4878aSAndroid Build Coastguard Worker         static_cast<unsigned>(offset_),
441*61c4878aSAndroid Build Coastguard Worker         has_payload() ? static_cast<unsigned>(payload_.size()) : 0,
442*61c4878aSAndroid Build Coastguard Worker         static_cast<unsigned>(window_end_offset_),
443*61c4878aSAndroid Build Coastguard Worker         remaining_bytes_.has_value()
444*61c4878aSAndroid Build Coastguard Worker             ? static_cast<unsigned>(remaining_bytes_.value())
445*61c4878aSAndroid Build Coastguard Worker             : -1,
446*61c4878aSAndroid Build Coastguard Worker         status_.has_value() ? static_cast<unsigned>(status_.value().code())
447*61c4878aSAndroid Build Coastguard Worker                             : -1);
448*61c4878aSAndroid Build Coastguard Worker   }
449*61c4878aSAndroid Build Coastguard Worker }
450*61c4878aSAndroid Build Coastguard Worker 
451*61c4878aSAndroid Build Coastguard Worker }  // namespace pw::transfer::internal
452