blob: 9b1b027f6e84a97e799ef0e777423130d2a637fb [file] [log] [blame]
shaneajg0869f2c2020-07-08 10:39:14 -04001// 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.
shaneajge6d3a612020-07-23 18:40:28 -040014
Alexei Frolovd3e5cb72021-01-08 13:08:45 -080015#include "pw_hdlc/encoder.h"
shaneajge6d3a612020-07-23 18:40:28 -040016
17#include <algorithm>
18#include <array>
19#include <cstddef>
20#include <cstring>
21#include <span>
22
Wyatt Heplerbfb669d2020-09-03 13:32:00 -070023#include "pw_bytes/endian.h"
Alexei Frolov44cd9522021-01-13 11:31:59 -080024#include "pw_hdlc/internal/encoder.h"
Alexei Frolovb9fda582021-03-13 18:02:52 -080025#include "pw_varint/varint.h"
shaneajge6d3a612020-07-23 18:40:28 -040026
27using std::byte;
28
Alexei Frolovd3e5cb72021-01-08 13:08:45 -080029namespace pw::hdlc {
Alexei Frolov44cd9522021-01-13 11:31:59 -080030namespace internal {
shaneajge6d3a612020-07-23 18:40:28 -040031
Wyatt Heplerbfb669d2020-09-03 13:32:00 -070032Status EscapeAndWrite(const byte b, stream::Writer& writer) {
Wyatt Heplercdb0f4e2020-09-14 08:49:17 -070033 if (b == kFlag) {
Wyatt Heplerbfb669d2020-09-03 13:32:00 -070034 return writer.Write(kEscapedFlag);
35 }
Wyatt Heplercdb0f4e2020-09-14 08:49:17 -070036 if (b == kEscape) {
Wyatt Heplerbfb669d2020-09-03 13:32:00 -070037 return writer.Write(kEscapedEscape);
shaneajge6d3a612020-07-23 18:40:28 -040038 }
39 return writer.Write(b);
40}
41
Wyatt Heplerbfb669d2020-09-03 13:32:00 -070042Status Encoder::WriteData(ConstByteSpan data) {
43 auto begin = data.begin();
shaneajge6d3a612020-07-23 18:40:28 -040044 while (true) {
Wyatt Heplerbfb669d2020-09-03 13:32:00 -070045 auto end = std::find_if(begin, data.end(), NeedsEscaping);
shaneajge6d3a612020-07-23 18:40:28 -040046
Wyatt Heplerbfb669d2020-09-03 13:32:00 -070047 if (Status status = writer_.Write(std::span(begin, end)); !status.ok()) {
shaneajge6d3a612020-07-23 18:40:28 -040048 return status;
49 }
Wyatt Heplerbfb669d2020-09-03 13:32:00 -070050 if (end == data.end()) {
51 fcs_.Update(data);
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -080052 return OkStatus();
shaneajge6d3a612020-07-23 18:40:28 -040053 }
Wyatt Heplerbfb669d2020-09-03 13:32:00 -070054 if (Status status = EscapeAndWrite(*end, writer_); !status.ok()) {
shaneajge6d3a612020-07-23 18:40:28 -040055 return status;
56 }
57 begin = end + 1;
58 }
Wyatt Heplerbfb669d2020-09-03 13:32:00 -070059}
shaneajge6d3a612020-07-23 18:40:28 -040060
Wyatt Heplerbfb669d2020-09-03 13:32:00 -070061Status Encoder::FinishFrame() {
Wyatt Heplereb8a34c2020-09-02 13:11:03 -070062 if (Status status =
Wyatt Heplerbfb669d2020-09-03 13:32:00 -070063 WriteData(bytes::CopyInOrder(std::endian::little, fcs_.value()));
Wyatt Heplereb8a34c2020-09-02 13:11:03 -070064 !status.ok()) {
shaneajge6d3a612020-07-23 18:40:28 -040065 return status;
66 }
Wyatt Heplercdb0f4e2020-09-14 08:49:17 -070067 return writer_.Write(kFlag);
Wyatt Heplerbfb669d2020-09-03 13:32:00 -070068}
69
Alexei Frolovb9fda582021-03-13 18:02:52 -080070size_t Encoder::MaxEncodedSize(uint64_t address, ConstByteSpan payload) {
Alexei Frolov44cd9522021-01-13 11:31:59 -080071 constexpr size_t kFcsMaxSize = 8; // Worst case FCS: 0x7e7e7e7e.
Alexei Frolovb9fda582021-03-13 18:02:52 -080072 size_t max_encoded_address_size = varint::EncodedSize(address) * 2;
Alexei Frolov44cd9522021-01-13 11:31:59 -080073 size_t encoded_payload_size =
74 payload.size() +
75 std::count_if(payload.begin(), payload.end(), NeedsEscaping);
76
Alexei Frolovb9fda582021-03-13 18:02:52 -080077 return max_encoded_address_size + sizeof(kUnusedControl) +
78 encoded_payload_size + kFcsMaxSize;
79}
80
81Status Encoder::StartFrame(uint64_t address, std::byte control) {
82 fcs_.clear();
83 if (Status status = writer_.Write(kFlag); !status.ok()) {
84 return status;
85 }
86
87 std::array<std::byte, 16> metadata_buffer;
88 size_t metadata_size =
89 varint::Encode(address, metadata_buffer, kAddressFormat);
90 if (metadata_size == 0) {
91 return Status::InvalidArgument();
92 }
93
94 metadata_buffer[metadata_size++] = control;
95 return WriteData(std::span(metadata_buffer).first(metadata_size));
Alexei Frolov44cd9522021-01-13 11:31:59 -080096}
97
98} // namespace internal
Wyatt Heplerbfb669d2020-09-03 13:32:00 -070099
Alexei Frolovb9fda582021-03-13 18:02:52 -0800100Status WriteUIFrame(uint64_t address,
Alexei Frolov6053c312020-12-09 22:43:55 -0800101 ConstByteSpan payload,
102 stream::Writer& writer) {
Alexei Frolov44cd9522021-01-13 11:31:59 -0800103 if (internal::Encoder::MaxEncodedSize(address, payload) >
104 writer.ConservativeWriteLimit()) {
105 return Status::ResourceExhausted();
106 }
107
108 internal::Encoder encoder(writer);
Wyatt Heplerbfb669d2020-09-03 13:32:00 -0700109
Alexei Frolov6053c312020-12-09 22:43:55 -0800110 if (Status status = encoder.StartUnnumberedFrame(address); !status.ok()) {
shaneajge6d3a612020-07-23 18:40:28 -0400111 return status;
112 }
Wyatt Heplerbfb669d2020-09-03 13:32:00 -0700113 if (Status status = encoder.WriteData(payload); !status.ok()) {
114 return status;
115 }
116 return encoder.FinishFrame();
shaneajge6d3a612020-07-23 18:40:28 -0400117}
118
Alexei Frolovd3e5cb72021-01-08 13:08:45 -0800119} // namespace pw::hdlc