Skip to content

Commit f31b4a6

Browse files
hlord2000kartben
authored andcommitted
lib: utils: add consistent overhead byte stuffing
Implementation uses netbufs. Signed-off-by: Helmut Lord <kellyhlord@gmail.com>
1 parent 783fe57 commit f31b4a6

File tree

4 files changed

+208
-0
lines changed

4 files changed

+208
-0
lines changed

include/zephyr/data/cobs.h

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* Copyright (c) 2024 Kelly Helmut Lord
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#ifndef ZEPHYR_INCLUDE_DATA_COBS_H_
8+
#define ZEPHYR_INCLUDE_DATA_COBS_H_
9+
10+
#include <stddef.h>
11+
#include <sys/types.h>
12+
#include <zephyr/sys/util.h>
13+
#include <zephyr/net_buf.h>
14+
15+
#ifdef __cplusplus
16+
extern "C" {
17+
#endif
18+
19+
#define COBS_DEFAULT_DELIMITER 0x00
20+
21+
/**
22+
* Flag indicating that encode and decode should include an implicit end delimiter
23+
*/
24+
#define COBS_FLAG_TRAILING_DELIMITER BIT(8)
25+
26+
/**
27+
* Macro for extracting delimiter from flags. 8 LSB of "flags" is used for the delimiter
28+
* Example usage:
29+
* cobs_encode(src_buf, dst_buf, COBS_FLAG_TRAILING_DELIMITER | COBS_FLAG_CUSTOM_DELIMITER(0x7F));
30+
*/
31+
#define COBS_FLAG_CUSTOM_DELIMITER(x) ((x) & 0xff)
32+
33+
/**
34+
* @defgroup cobs COBS (Consistent Overhead Byte Stuffing)
35+
* @ingroup utilities
36+
* @{
37+
*
38+
* @brief COBS encoding and decoding functions with custom delimiter support
39+
*
40+
* Provides functions for COBS encoding/decoding with configurable delimiters.
41+
* The implementation handles both standard zero-delimited COBS and custom
42+
* delimiter variants.
43+
*/
44+
45+
/**
46+
* @brief Calculate maximum encoded buffer size
47+
*
48+
* @param decoded_size Size of input data to be encoded
49+
* @param flags COBS_FLAG_TRAILING_DELIMITER to include termination byte in calculation
50+
*
51+
* @return Required buffer size for worst-case encoding scenario
52+
*/
53+
static inline size_t cobs_max_encoded_len(size_t decoded_size, uint32_t flags)
54+
{
55+
if (flags & COBS_FLAG_TRAILING_DELIMITER) {
56+
return decoded_size + decoded_size / 254 + 1 + 1;
57+
} else {
58+
return decoded_size + decoded_size / 254 + 1;
59+
}
60+
}
61+
62+
/**
63+
* @brief Standard COBS encoding
64+
*
65+
* @param src Source buffer to decode
66+
* @param dst Destination buffer for decoded data
67+
* @param flags Decoding flags (reserved)
68+
*
69+
* @retval 0 Success
70+
* @retval -ENOMEM Insufficient destination space
71+
* @retval -EINVAL Invalid COBS structure or parameters
72+
*/
73+
74+
int cobs_encode(struct net_buf *src, struct net_buf *dst, uint32_t flags);
75+
76+
/**
77+
* @brief Standard COBS decoding
78+
*
79+
* @param src Source buffer to decode
80+
* @param dst Destination buffer for decoded data
81+
* @param flags Decoding flags (reserved)
82+
*
83+
* @retval 0 Success
84+
* @retval -ENOMEM Insufficient destination space
85+
* @retval -EINVAL Invalid COBS structure or parameters
86+
*/
87+
int cobs_decode(struct net_buf *src, struct net_buf *dst, uint32_t flags);
88+
89+
/** @} */
90+
91+
#ifdef __cplusplus
92+
}
93+
#endif
94+
95+
#endif /* ZEPHYR_INCLUDE_DATA_COBS_H_ */

lib/utils/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ zephyr_sources(
1111
)
1212

1313
zephyr_sources_ifdef(CONFIG_ONOFF onoff.c)
14+
1415
zephyr_sources_ifdef(CONFIG_NOTIFY notify.c)
1516

1617
zephyr_sources_ifdef(CONFIG_JSON_LIBRARY json.c)
@@ -21,6 +22,8 @@ zephyr_sources_ifdef(CONFIG_UTF8 utf8.c)
2122

2223
zephyr_sources_ifdef(CONFIG_WINSTREAM winstream.c)
2324

25+
zephyr_sources_ifdef(CONFIG_COBS cobs.c)
26+
2427
zephyr_library_include_directories(
2528
${ZEPHYR_BASE}/kernel/include
2629
${ZEPHYR_BASE}/arch/${ARCH}/include

lib/utils/Kconfig

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,10 @@ config UTF8
5757
Enable the utf8 API. The API implements functions to specifically
5858
handle UTF-8 encoded strings.
5959

60+
config COBS
61+
bool "Consistent overhead byte stuffing"
62+
select NET_BUF
63+
help
64+
Enable consistent overhead byte stuffing
65+
6066
endmenu

lib/utils/cobs.c

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
* Copyright (c) 2024 Kelly Helmut Lord
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <errno.h>
8+
#include <stdint.h>
9+
#include <zephyr/data/cobs.h>
10+
11+
int cobs_encode(struct net_buf *src, struct net_buf *dst, uint32_t flags)
12+
{
13+
uint8_t delimiter = COBS_FLAG_CUSTOM_DELIMITER(flags);
14+
15+
/* Calculate required space for worst case */
16+
size_t max_encoded_size = cobs_max_encoded_len(src->len, flags);
17+
18+
/* Check if destination has enough space */
19+
if (net_buf_tailroom(dst) < max_encoded_size) {
20+
return -ENOMEM;
21+
}
22+
23+
uint8_t *code_ptr = net_buf_add(dst, 1);
24+
uint8_t code = 1;
25+
26+
/* Process all input bytes */
27+
uint8_t data = 0;
28+
29+
while (src->len > 0) {
30+
data = net_buf_pull_u8(src);
31+
if (data == delimiter) {
32+
/* Delimiter found - write current code and start new block */
33+
*code_ptr = code;
34+
code_ptr = net_buf_add(dst, 1);
35+
code = 1;
36+
} else {
37+
/* Add non-zero byte to output */
38+
net_buf_add_u8(dst, data);
39+
code++;
40+
41+
/* If we've reached maximum block size, start a new block */
42+
if (code == 0xFF && (src->len - 1 >= 0)) {
43+
*code_ptr = code;
44+
code_ptr = net_buf_add(dst, 1);
45+
code = 1;
46+
}
47+
}
48+
}
49+
50+
*code_ptr = code;
51+
52+
if (flags & COBS_FLAG_TRAILING_DELIMITER) {
53+
/* Add final delimiter */
54+
net_buf_add_u8(dst, delimiter);
55+
}
56+
57+
return 0;
58+
}
59+
60+
int cobs_decode(struct net_buf *src, struct net_buf *dst, uint32_t flags)
61+
{
62+
uint8_t delimiter = COBS_FLAG_CUSTOM_DELIMITER(flags);
63+
64+
if (flags & COBS_FLAG_TRAILING_DELIMITER) {
65+
uint8_t end_delim = net_buf_remove_u8(src);
66+
67+
if (end_delim != delimiter) {
68+
return -EINVAL;
69+
}
70+
}
71+
72+
while (src->len > 0) {
73+
/* Pull the COBS offset byte */
74+
uint8_t offset = net_buf_pull_u8(src);
75+
76+
if (offset == delimiter && !(flags & COBS_FLAG_TRAILING_DELIMITER)) {
77+
return -EINVAL;
78+
}
79+
80+
/* Verify we have enough data */
81+
if (src->len < (offset - 1)) {
82+
return -EINVAL;
83+
}
84+
85+
/* Copy offset-1 bytes */
86+
for (uint8_t i = 0; i < offset - 1; i++) {
87+
uint8_t byte = net_buf_pull_u8(src);
88+
89+
if (byte == delimiter) {
90+
return -EINVAL;
91+
}
92+
net_buf_add_u8(dst, byte);
93+
}
94+
95+
/* If this wasn't a maximum offset and we have more data,
96+
* there was a delimiter here in the original data
97+
*/
98+
if (offset != 0xFF && src->len > 0) {
99+
net_buf_add_u8(dst, delimiter);
100+
}
101+
}
102+
103+
return 0;
104+
}

0 commit comments

Comments
 (0)