cobs.c 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. /*
  2. * cobs.c
  3. *
  4. * Consistent Overhead Byte Stuffing
  5. * https://github.com/cmcqueen/cobs-c
  6. */
  7. #include "cobs.h"
  8. /*****************************************************************************
  9. * Defines
  10. ****************************************************************************/
  11. #ifndef FALSE
  12. #define FALSE (0)
  13. #endif
  14. #ifndef TRUE
  15. #define TRUE (!FALSE)
  16. #endif
  17. /*****************************************************************************
  18. * Functions
  19. ****************************************************************************/
  20. cobs_encode_result cobs_encode(uint8_t *dst_buf_ptr, size_t dst_buf_len, const uint8_t * src_ptr, size_t src_len)
  21. {
  22. cobs_encode_result result = { 0, COBS_ENCODE_OK };
  23. const uint8_t * src_end_ptr = src_ptr + src_len;
  24. uint8_t * dst_buf_end_ptr = dst_buf_ptr + dst_buf_len;
  25. uint8_t * dst_code_write_ptr = dst_buf_ptr;
  26. uint8_t * dst_write_ptr = dst_code_write_ptr + 1;
  27. uint8_t src_byte = 0;
  28. uint8_t search_len = 1;
  29. /* First, do a NULL pointer check and return immediately if it fails. */
  30. if ((dst_buf_ptr == NULL) || (src_ptr == NULL))
  31. {
  32. result.status = COBS_ENCODE_NULL_POINTER;
  33. return result;
  34. }
  35. if (src_len != 0)
  36. {
  37. /* Iterate over the source bytes */
  38. for (;;)
  39. {
  40. /* Check for running out of output buffer space */
  41. if (dst_write_ptr >= dst_buf_end_ptr)
  42. {
  43. result.status |= COBS_ENCODE_OUT_BUFFER_OVERFLOW;
  44. break;
  45. }
  46. src_byte = *src_ptr++;
  47. if (src_byte == 0)
  48. {
  49. /* We found a zero byte */
  50. *dst_code_write_ptr = search_len;
  51. dst_code_write_ptr = dst_write_ptr++;
  52. search_len = 1;
  53. if (src_ptr >= src_end_ptr)
  54. {
  55. break;
  56. }
  57. }
  58. else
  59. {
  60. /* Copy the non-zero byte to the destination buffer */
  61. *dst_write_ptr++ = src_byte;
  62. search_len++;
  63. if (src_ptr >= src_end_ptr)
  64. {
  65. break;
  66. }
  67. if (search_len == 0xFF)
  68. {
  69. /* We have a long string of non-zero bytes, so we need
  70. * to write out a length code of 0xFF. */
  71. *dst_code_write_ptr = search_len;
  72. dst_code_write_ptr = dst_write_ptr++;
  73. search_len = 1;
  74. }
  75. }
  76. }
  77. }
  78. /* We've reached the end of the source data (or possibly run out of output buffer)
  79. * Finalise the remaining output. In particular, write the code (length) byte.
  80. * Update the pointer to calculate the final output length.
  81. */
  82. if (dst_code_write_ptr >= dst_buf_end_ptr)
  83. {
  84. /* We've run out of output buffer to write the code byte. */
  85. result.status |= COBS_ENCODE_OUT_BUFFER_OVERFLOW;
  86. dst_write_ptr = dst_buf_end_ptr;
  87. }
  88. else
  89. {
  90. /* Write the last code (length) byte. */
  91. *dst_code_write_ptr = search_len;
  92. }
  93. /* Calculate the output length, from the value of dst_code_write_ptr */
  94. result.out_len = dst_write_ptr - dst_buf_ptr;
  95. return result;
  96. }
  97. cobs_decode_result cobs_decode(uint8_t *dst_buf_ptr, size_t dst_buf_len, const uint8_t * src_ptr, size_t src_len)
  98. {
  99. cobs_decode_result result = { 0, COBS_DECODE_OK };
  100. const uint8_t * src_end_ptr = src_ptr + src_len;
  101. uint8_t * dst_buf_end_ptr = dst_buf_ptr + dst_buf_len;
  102. uint8_t * dst_write_ptr = dst_buf_ptr;
  103. size_t remaining_bytes;
  104. uint8_t src_byte;
  105. uint8_t i;
  106. uint8_t len_code;
  107. /* First, do a NULL pointer check and return immediately if it fails. */
  108. if ((dst_buf_ptr == NULL) || (src_ptr == NULL))
  109. {
  110. result.status = COBS_DECODE_NULL_POINTER;
  111. return result;
  112. }
  113. if (src_len != 0)
  114. {
  115. for (;;)
  116. {
  117. len_code = *src_ptr++;
  118. if (len_code == 0)
  119. {
  120. result.status |= COBS_DECODE_ZERO_BYTE_IN_INPUT;
  121. break;
  122. }
  123. len_code--;
  124. /* Check length code against remaining input bytes */
  125. remaining_bytes = src_end_ptr - src_ptr;
  126. if (len_code > remaining_bytes)
  127. {
  128. result.status |= COBS_DECODE_INPUT_TOO_SHORT;
  129. len_code = remaining_bytes;
  130. }
  131. /* Check length code against remaining output buffer space */
  132. remaining_bytes = dst_buf_end_ptr - dst_write_ptr;
  133. if (len_code > remaining_bytes)
  134. {
  135. result.status |= COBS_DECODE_OUT_BUFFER_OVERFLOW;
  136. len_code = remaining_bytes;
  137. }
  138. for (i = len_code; i != 0; i--)
  139. {
  140. src_byte = *src_ptr++;
  141. if (src_byte == 0)
  142. {
  143. result.status |= COBS_DECODE_ZERO_BYTE_IN_INPUT;
  144. }
  145. *dst_write_ptr++ = src_byte;
  146. }
  147. if (src_ptr >= src_end_ptr)
  148. {
  149. break;
  150. }
  151. /* Add a zero to the end */
  152. if (len_code != 0xFE)
  153. {
  154. if (dst_write_ptr >= dst_buf_end_ptr)
  155. {
  156. result.status |= COBS_DECODE_OUT_BUFFER_OVERFLOW;
  157. break;
  158. }
  159. *dst_write_ptr++ = 0;
  160. }
  161. }
  162. }
  163. result.out_len = dst_write_ptr - dst_buf_ptr;
  164. return result;
  165. }