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)25Result<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)47Status 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)114Result<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