SatCat5
codec_cobs.cc
1 // Copyright 2025 The Aerospace Corporation.
3 // This file is a part of SatCat5, licensed under CERN-OHL-W v2 or later.
5 
6 #include <satcat5/codec_cobs.h>
7 #include <satcat5/log.h>
8 
15 
16 // By default, log all COBS errors.
17 #ifndef SATCAT5_COBS_LOG_ERROR
18 #define SATCAT5_COBS_LOG_ERROR 1
19 #endif
20 
21 static constexpr u8 COBS_END = 0x00; // Inter-frame token
22 static constexpr u8 COBS_MAX = 0xFE; // Maximum segment = 254 bytes
23 static constexpr u8 BLK_EMPTY = 0x00; // First block / empty packet
24 static constexpr u8 BLK_DATA = 0x01; // Data block, no termination
25 static constexpr u8 BLK_TERM = 0x02; // Data block, zero-terminated
26 
27 CobsEncoder::CobsEncoder(Writeable* dst)
28  : m_dst(dst)
29  , m_ctr(0)
30  , m_ovr(0)
31  , m_buf{}
32 {
33  // Nothing else to initialize.
34 }
35 
36 unsigned CobsEncoder::get_write_space() const {
37  // Worst-case: One overhead byte per 254 output bytes (including
38  // those in the working buffer), plus one for the COBS_END token.
39  unsigned dst = m_dst->get_write_space();
40  unsigned rem = 3 + unsigned(m_ctr) + dst / 254;
41  if (m_ovr || dst <= rem)
42  return 0;
43  else
44  return dst - rem;
45 }
46 
48  if (m_ovr) {
49  // Overflow: Terminate then abort, to gracefully handle
50  // output interfaces where write_abort() may be a no-op.
51  m_ctr = 0;
52  m_ovr = 0;
53  m_dst->write_u8(COBS_END);
54  m_dst->write_abort();
55  return false;
56  } else {
57  // Normal case: Flush the last segment, then terminate.
58  flush_segment();
59  m_dst->write_u8(COBS_END);
60  return m_dst->write_finalize();
61  }
62 }
63 
65  m_ovr = 1; // Set persistent error flag
66 }
67 
68 void CobsEncoder::write_next(u8 data) {
69  // Max segment? Flush and start a new one.
70  if (m_ctr == COBS_MAX) {
71  flush_segment();
72  }
73  // Append new data to the current segment.
74  if (data == COBS_END) {
75  flush_segment();
76  } else {
77  m_buf[m_ctr++] = data;
78  }
79 }
80 
81 void CobsEncoder::flush_segment() {
82  m_dst->write_u8(m_ctr + 1);
83  if (m_ctr) {
84  m_dst->write_bytes(m_ctr, m_buf);
85  m_ctr = 0;
86  }
87 }
88 
90  : m_dst(dst)
91  , m_blk(BLK_EMPTY)
92  , m_ctr(0)
93  , m_ovr(0)
94 {
95  // Nothing else to initialize.
96 }
97 
98 unsigned CobsDecoder::get_write_space() const {
99  // Decoded output will always be smaller than encoded input.
100  return m_dst->get_write_space();
101 }
102 
103 void CobsDecoder::write_next(u8 data) {
104  if (data == COBS_END && m_ctr) {
105  // Unexpected end-of-frame token, reset parsing.
106  if (SATCAT5_COBS_LOG_ERROR)
107  satcat5::log::Log(satcat5::log::WARNING, "COBS decode error");
108  m_dst->write_abort();
109  m_blk = BLK_EMPTY;
110  m_ctr = 0;
111  m_ovr = 0;
112  } else if (data == COBS_END) {
113  // Normal end-of-frame token.
114  if (m_blk != BLK_EMPTY) m_dst->write_finalize();
115  m_blk = BLK_EMPTY;
116  m_ctr = 0;
117  m_ovr = 0;
118  } else if (m_ovr) {
119  // Ignore incoming data after an overflow event.
120  } else if (m_ctr) {
121  // Unmodified data.
122  m_dst->write_u8(data);
123  --m_ctr;
124  } else {
125  // Start of new segment.
126  if (m_blk == BLK_TERM) m_dst->write_u8(COBS_END);
127  m_blk = (data == 0xFF) ? BLK_DATA : BLK_TERM;
128  m_ctr = data - 1;
129  }
130 }
131 
133  // Discard any further data until next end-of-frame.
134  m_ctr = COBS_MAX;
135  m_ovr = 1;
136 }
137 
139  : CobsEncoder(dst) // Upstream writes are encoded enroute
140  , ReadableRedirect(&m_buff) // Upstream reads pull from buffer
141  , m_buff(m_rawbuff, SATCAT5_COBS_BUFFSIZE, SATCAT5_COBS_PACKETS)
142  , m_decode(&m_buff) // Decoder writes to buffer
143  , m_copy(src, &m_decode) // Auto-copy from source to decoder
144 {
145  // Nothing else to initialize.
146 }
147 
149  : CobsDecoder(dst) // Upstream writes are decoded enroute
150  , ReadableRedirect(&m_buff) // Upstream reads pull from buffer
151  , m_buff(m_rawbuff, SATCAT5_COBS_BUFFSIZE, 0)
152  , m_encode(&m_buff) // Decoder writes to buffer
153  , m_copy(src, &m_encode) // Auto-copy from source to encoder
154 {
155  // Nothing else to initialize.
156 }
Buffered COBS encoder / decoder pair.
Definition: codec_cobs.h:106
CobsCodec(satcat5::io::Writeable *dst, satcat5::io::Readable *src)
Constructor links to specified source and destination.
Definition: codec_cobs.cc:138
Buffered COBS encoder / decoder pair with opposite polarity.
Definition: codec_cobs.h:131
CobsCodecInverse(satcat5::io::Writeable *dst, satcat5::io::Readable *src)
Constructor links to specified source and destination.
Definition: codec_cobs.cc:148
Inline COBS decoder.
Definition: codec_cobs.h:74
unsigned get_write_space() const override
How many bytes can be written without blocking?
Definition: codec_cobs.cc:98
CobsDecoder(satcat5::io::Writeable *dst)
Permanently link this encoder to an output object.
Definition: codec_cobs.cc:89
void write_next(u8 data) override
Write the next byte to the underlying buffer or device.
Definition: codec_cobs.cc:103
void write_overflow() override
Optional error handling for write overflow.
Definition: codec_cobs.cc:132
Inline COBS encoder.
Definition: codec_cobs.h:49
void write_overflow() override
Optional error handling for write overflow.
Definition: codec_cobs.cc:64
unsigned get_write_space() const override
How many bytes can be written without blocking?
Definition: codec_cobs.cc:36
void write_next(u8 data) override
Write the next byte to the underlying buffer or device.
Definition: codec_cobs.cc:68
bool write_finalize() override
Mark end of frame and release temporary working data.
Definition: codec_cobs.cc:47
Abstract API for reading byte-streams and packets.
Definition: io_readable.h:68
Wrapper class for forwarding reads to another object.
Definition: io_readable.h:299
Abstract API for writing byte-streams and packets.
Definition: io_writeable.h:24
virtual unsigned get_write_space() const =0
How many bytes can be written without blocking?
virtual void write_bytes(unsigned nbytes, const void *src)
Write 0 or more bytes from a buffer.
virtual void write_abort()
If possible, abort the current partially-written packet.
void write_u8(u8 data)
One of many functions for writing integer/floating point values, see details.
Definition: io_writeable.cc:20
virtual bool write_finalize()
Mark end of frame and release temporary working data.
The Log class creates and formats one log message.
Definition: log.h:195
Inline COBS encoder and decoder objects.
Diagnostic logging to UART and/or Ethernet ports.