• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 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_transfer/internal/chunk.h"
16 
17 #include "pw_protobuf/decoder.h"
18 #include "pw_status/try.h"
19 #include "pw_transfer/transfer.pwpb.h"
20 
21 namespace pw::transfer::internal {
22 
23 namespace ProtoChunk = transfer::Chunk;
24 
ExtractTransferId(ConstByteSpan message)25 Result<uint32_t> ExtractTransferId(ConstByteSpan message) {
26   protobuf::Decoder decoder(message);
27 
28   while (decoder.Next().ok()) {
29     ProtoChunk::Fields field =
30         static_cast<ProtoChunk::Fields>(decoder.FieldNumber());
31 
32     switch (field) {
33       case ProtoChunk::Fields::TRANSFER_ID: {
34         uint32_t transfer_id;
35         PW_TRY(decoder.ReadUint32(&transfer_id));
36         return transfer_id;
37       }
38 
39       default:
40         continue;
41     }
42   }
43 
44   return Status::DataLoss();
45 }
46 
DecodeChunk(ConstByteSpan message,Chunk & chunk)47 Status DecodeChunk(ConstByteSpan message, Chunk& chunk) {
48   protobuf::Decoder decoder(message);
49   Status status;
50   uint32_t value;
51 
52   chunk = {};
53 
54   while ((status = decoder.Next()).ok()) {
55     ProtoChunk::Fields field =
56         static_cast<ProtoChunk::Fields>(decoder.FieldNumber());
57 
58     switch (field) {
59       case ProtoChunk::Fields::TRANSFER_ID:
60         PW_TRY(decoder.ReadUint32(&chunk.transfer_id));
61         break;
62 
63       case ProtoChunk::Fields::PENDING_BYTES:
64         PW_TRY(decoder.ReadUint32(&value));
65         chunk.pending_bytes = value;
66         break;
67 
68       case ProtoChunk::Fields::MAX_CHUNK_SIZE_BYTES:
69         PW_TRY(decoder.ReadUint32(&value));
70         chunk.max_chunk_size_bytes = value;
71         break;
72 
73       case ProtoChunk::Fields::MIN_DELAY_MICROSECONDS:
74         PW_TRY(decoder.ReadUint32(&value));
75         chunk.min_delay_microseconds = value;
76         break;
77 
78       case ProtoChunk::Fields::OFFSET:
79         PW_TRY(decoder.ReadUint32(&chunk.offset));
80         break;
81 
82       case ProtoChunk::Fields::DATA:
83         PW_TRY(decoder.ReadBytes(&chunk.data));
84         break;
85 
86       case ProtoChunk::Fields::REMAINING_BYTES: {
87         uint64_t remaining;
88         PW_TRY(decoder.ReadUint64(&remaining));
89         chunk.remaining_bytes = remaining;
90         break;
91       }
92 
93       case ProtoChunk::Fields::STATUS:
94         PW_TRY(decoder.ReadUint32(&value));
95         chunk.status = static_cast<Status::Code>(value);
96         break;
97 
98       case ProtoChunk::Fields::WINDOW_END_OFFSET:
99         PW_TRY(decoder.ReadUint32(&chunk.window_end_offset));
100         break;
101 
102       case ProtoChunk::Fields::TYPE: {
103         uint32_t type;
104         PW_TRY(decoder.ReadUint32(&type));
105         chunk.type = static_cast<Chunk::Type>(type);
106         break;
107       }
108     }
109   }
110 
111   return status.IsOutOfRange() ? OkStatus() : status;
112 }
113 
EncodeChunk(const Chunk & chunk,ByteSpan buffer)114 Result<ConstByteSpan> EncodeChunk(const Chunk& chunk, ByteSpan buffer) {
115   ProtoChunk::MemoryEncoder encoder(buffer);
116 
117   encoder.WriteTransferId(chunk.transfer_id).IgnoreError();
118 
119   if (chunk.window_end_offset != 0) {
120     encoder.WriteWindowEndOffset(chunk.window_end_offset).IgnoreError();
121   }
122 
123   if (chunk.pending_bytes.has_value()) {
124     encoder.WritePendingBytes(chunk.pending_bytes.value()).IgnoreError();
125   }
126   if (chunk.max_chunk_size_bytes.has_value()) {
127     encoder.WriteMaxChunkSizeBytes(chunk.max_chunk_size_bytes.value())
128         .IgnoreError();
129   }
130   if (chunk.min_delay_microseconds.has_value()) {
131     encoder.WriteMinDelayMicroseconds(chunk.min_delay_microseconds.value())
132         .IgnoreError();
133   }
134   if (chunk.offset != 0) {
135     encoder.WriteOffset(chunk.offset).IgnoreError();
136   }
137   if (!chunk.data.empty()) {
138     encoder.WriteData(chunk.data).IgnoreError();
139   }
140   if (chunk.remaining_bytes.has_value()) {
141     encoder.WriteRemainingBytes(chunk.remaining_bytes.value()).IgnoreError();
142   }
143   if (chunk.status.has_value()) {
144     encoder.WriteStatus(chunk.status.value().code()).IgnoreError();
145   }
146 
147   if (chunk.type.has_value()) {
148     encoder.WriteType(static_cast<ProtoChunk::Type>(chunk.type.value()))
149         .IgnoreError();
150   }
151 
152   PW_TRY(encoder.status());
153   return ConstByteSpan(encoder);
154 }
155 
156 }  // namespace pw::transfer::internal
157