-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathn_cobs.c
161 lines (137 loc) · 5.48 KB
/
n_cobs.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
// Copyright 2023 Blues Inc. All rights reserved.
// Use of this source code is governed by licenses granted by the
// copyright holder including that found in the LICENSE file.
#include <stdint.h>
#define COBS_EOP_OVERHEAD 1
//**************************************************************************/
/*!
@brief Decode a string encoded with COBS encoding
@details Because the decoded length is guaranteed to be less than or equal to
length, the decode may be done in-place. Default behavior (with eop == 0) is
to restore all '\0' into output data, but if a different value is specified,
the input is XOR'ed such that THAT byte is the one that is assumed can't be
in the input.
@param ptr Pointer to the data to decode
@param length Length of the data to decode
@param eop Byte to use as the end-of-packet marker
@param dst Pointer to the buffer for the decoded data
@return the length of the decoded data
*/
/**************************************************************************/
uint32_t _cobsDecode(uint8_t *ptr, uint32_t length, uint8_t eop, uint8_t *dst)
{
const uint8_t *start = dst, *end = ptr + length;
uint8_t code = 0xFF, copy = 0;
for (; ptr < end; copy--) {
if (copy != 0) {
*dst++ = (*ptr++) ^ eop;
} else {
if (code != 0xFF) {
*dst++ = 0;
}
copy = code = (*ptr++) ^ eop;
if (code == 0) {
break;
}
}
}
return dst - start;
}
//**************************************************************************/
/*!
@brief Encode a string with Consistent Overhead Byte Stuffing (COBS) encoding
@details behavior (with eop == 0) is to eliminate all '\0' from input data,
but if a different value is specified, the output is XOR'ed such that THAT
byte is the one that won't be contained in output.
https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing
@param ptr Pointer to the data to encode
@param length Length of the data to encode
@param eop Byte to use as the end-of-packet marker
@param dst Pointer to the buffer for the encoded data
@return the length of the encoded data
@note You may use `_cobsEncodedLength()` to calculate the required size for
the buffer pointed to by the `dst` parameter.
@see _cobsEncodedLength()
*/
/**************************************************************************/
uint32_t _cobsEncode(uint8_t *ptr, uint32_t length, uint8_t eop, uint8_t *dst)
{
uint8_t ch;
uint8_t *start = dst;
uint8_t code = 1;
uint8_t *code_ptr = dst++; // Where to insert the leading count
while (length--) {
ch = *ptr++;
if (ch != 0) { // Input byte not zero
*dst++ = ch ^ eop;
code++;
}
if (ch == 0 || code == 0xFF) { // Input is zero or complete block
*code_ptr = code ^ eop;
code = 1;
code_ptr = dst++;
}
}
*code_ptr = code ^ eop; // Final code
return (dst - start);
}
//**************************************************************************/
/*!
@brief Compute the encoding length of unencoded data
@param ptr Pointer to the data to encode
@param length Length of the data to encode
@return the length required for encoded data
@note The computed length does not include the EOP (end-of-packet) marker
*/
/**************************************************************************/
uint32_t _cobsEncodedLength(const uint8_t *ptr, uint32_t length)
{
uint8_t ch;
uint32_t dst = 1;
uint8_t code = 1;
while (length--) {
ch = *ptr++;
if (ch != 0) { // Input byte not zero
dst++;
code++;
}
if (ch == 0 || code == 0xFF) { // Input is zero or complete block
code = 1;
dst++;
}
}
return dst;
}
//**************************************************************************/
/*!
@brief Compute the max encoding length for a given length of unencoded data
@param length Length of the data to encode
@return The max length required to encode the data
@note Since the contents of the buffer are unknown, then we must assume
that the entire buffer has no end-of-packet markers. This would
require the injection of overhead bytes (as opposed to the
replacement of end-of-packet markers with overhead bytes) at
intervals of 255, thus producing the worst case scenario.
@note An additional byte is added for the EOP (end-of-packet) marker.
*/
/**************************************************************************/
uint32_t _cobsEncodedMaxLength(uint32_t length)
{
const uint32_t overheadBytes = ((length / 254) + ((length % 254) > 0));
return (length + overheadBytes + COBS_EOP_OVERHEAD);
}
//**************************************************************************/
/*!
@brief Compute the maximum length of unencoded data that can fit into a
buffer of specified length.
@param bufLen Length of the buffer in bytes
@return the length of unencoded data
@note The computation may leave additional space at the end.
@note An additional byte is added for the EOP (end-of-packet) marker.
*/
/**************************************************************************/
uint32_t _cobsGuaranteedFit(uint32_t bufLen)
{
uint32_t cobsOverhead = 1 + (bufLen / 254) + COBS_EOP_OVERHEAD;
return (cobsOverhead > bufLen ? 0 : (bufLen - cobsOverhead));
}