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 
cobs_encode(struct net_buf * src,struct net_buf * dst,uint32_t flags)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 
cobs_decode(struct net_buf * src,struct net_buf * dst,uint32_t flags)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 }
105