LCOV - code coverage report
Current view: top level - src - bitbuffer.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 100.0 % 73 73
Test Date: 2026-06-29 15:02:00 Functions: 100.0 % 8 8

            Line data    Source code
       1              : /**
       2              :  * @file bitbuffer.c
       3              :  * @brief Variable-length bit buffer for building compressed output.
       4              :  *
       5              :  * @cond INTERNAL
       6              :  * ============================================================================
       7              :  *  _____                                   ____
       8              :  * |_   _|_ _ _ __   __ _  __ _ _ __ __ _  / ___| _ __   __ _  ___ ___
       9              :  *   | |/ _` | '_ \ / _` |/ _` | '__/ _` | \___ \| '_ \ / _` |/ __/ _ \
      10              :  *   | | (_| | | | | (_| | (_| | | | (_| |  ___) | |_) | (_| | (_|  __/
      11              :  *   |_|\__,_|_| |_|\__,_|\__, |_|  \__,_| |____/| .__/ \__,_|\___\___|
      12              :  *                        |___/                  |_|
      13              :  * ============================================================================
      14              :  * @endcond
      15              :  *
      16              :  * This module provides a dynamically-growing bit buffer for constructing
      17              :  * compressed output streams. Bits are appended sequentially using MSB-first
      18              :  * ordering as required by CCSDS 124.0-B-1.
      19              :  *
      20              :  * @par Bit Ordering
      21              :  * Bits are appended MSB-first within each byte:
      22              :  * - First bit appended goes to bit position 7
      23              :  * - Second bit goes to position 6, etc.
      24              :  *
      25              :  * @authors Georges Labrèche <georges@tanagraspace.com> — https://georges.fyi
      26              :  * @authors Claude Code (Anthropic) <noreply@anthropic.com>
      27              :  *
      28              :  * @see https://ccsds.org/Pubs/124x0b1.pdf CCSDS 124.0-B-1 Standard
      29              :  */
      30              : 
      31              : #include "ccsds124.h"
      32              : #include <string.h>
      33              : 
      34              : /**
      35              :  * @name Initialization Functions
      36              :  * @{
      37              :  */
      38              : 
      39              : 
      40       378534 : void bitbuffer_init(bitbuffer_t *bb) {
      41       378534 :     if (bb != NULL) {
      42       378534 :         bb->num_bits = 0U;
      43       378534 :         bb->acc = 0U;
      44       378534 :         bb->acc_len = 0U;
      45       378534 :         (void)memset(bb->data, 0, CCSDS124_MAX_OUTPUT_BYTES);
      46              :     }
      47       378534 : }
      48              : 
      49              : 
      50       189280 : void bitbuffer_clear(bitbuffer_t *bb) {
      51       189280 :     bitbuffer_init(bb);
      52       189280 : }
      53              : 
      54              : /** @} */ /* End of Initialization Functions */
      55              : 
      56              : /**
      57              :  * @name Bit Appending Functions
      58              :  * @{
      59              :  */
      60              : 
      61              : 
      62              : /**
      63              :  * @brief Flush accumulator to data buffer when it has 8+ bits.
      64              :  * @param[in,out] bb Bit buffer
      65              :  */
      66              : static void bitbuffer_flush_acc(bitbuffer_t *bb) {
      67     12377952 :     while (bb->acc_len >= 8U) {
      68              :         /* Extract top 8 bits */
      69      6188976 :         bb->acc_len -= 8U;
      70      6188976 :         size_t byte_index = (bb->num_bits - bb->acc_len - 8U) / 8U;
      71      6188976 :         bb->data[byte_index] = (uint8_t)(bb->acc >> bb->acc_len);
      72      6188976 :         bb->acc &= ((1U << bb->acc_len) - 1U);  /* Clear extracted bits */
      73              :     }
      74              : }
      75              : 
      76              : 
      77     48165520 : int bitbuffer_append_bit(bitbuffer_t *bb, int bit) {
      78              :     int result = CCSDS124_ERROR_INVALID_ARG;
      79              : 
      80     48165520 :     if (bb != NULL) {
      81              :         /* Check for overflow - CCSDS124_MAX_OUTPUT_BYTES * 8 bits */
      82              :         size_t max_bits = CCSDS124_MAX_OUTPUT_BYTES * 8U;
      83     48165520 :         if (bb->num_bits >= max_bits) {
      84              :             result = CCSDS124_ERROR_OVERFLOW;
      85              :         } else {
      86              :             /* Accumulate bit in MSB-first order */
      87     48165420 :             uint32_t bit_val = ((uint32_t)bit) & 1U;
      88     48165420 :             bb->acc = (bb->acc << 1U) | bit_val;
      89     48165420 :             bb->acc_len++;
      90     48165420 :             bb->num_bits++;
      91              : 
      92              :             /* Flush when accumulator has 8+ bits */
      93     48165420 :             if (bb->acc_len >= 8U) {
      94              :                 bitbuffer_flush_acc(bb);
      95              :             }
      96              : 
      97              :             result = CCSDS124_OK;
      98              :         }
      99              :     }
     100              : 
     101     48165520 :     return result;
     102              : }
     103              : 
     104              : 
     105            3 : int bitbuffer_append_bits(bitbuffer_t *bb, const uint8_t *data, size_t num_bits) {
     106              :     int result = CCSDS124_ERROR_INVALID_ARG;
     107              : 
     108            3 :     if ((bb != NULL) && (data != NULL)) {
     109              :         /* Check for overflow - CCSDS124_MAX_OUTPUT_BYTES * 8 bits */
     110              :         size_t max_bits = CCSDS124_MAX_OUTPUT_BYTES * 8U;
     111            3 :         if ((bb->num_bits + num_bits) > max_bits) {
     112              :             result = CCSDS124_ERROR_OVERFLOW;
     113              :         } else {
     114              :             result = CCSDS124_OK;
     115              : 
     116              :             /* Append each bit MSB-first */
     117           23 :             for (size_t i = 0U; (i < num_bits) && (result == CCSDS124_OK); i++) {
     118           21 :                 size_t byte_index = i / 8U;
     119           21 :                 size_t bit_index = i % 8U;
     120              : 
     121              :                 /* Extract bits MSB-first (bit 7, 6, 5, ..., 0) */
     122           21 :                 uint32_t shift_amount = 7U - (uint32_t)bit_index;
     123           21 :                 uint32_t shifted = (uint32_t)data[byte_index] >> shift_amount;
     124           21 :                 uint32_t masked = shifted & 1U;
     125           21 :                 int bit = (int)masked;
     126              : 
     127           21 :                 result = bitbuffer_append_bit(bb, bit);
     128              :             }
     129              :         }
     130              :     }
     131              : 
     132            3 :     return result;
     133              : }
     134              : 
     135              : 
     136              : /**
     137              :  * @brief Append multiple bits from a value in a single operation.
     138              :  *
     139              :  * Batches bit operations to reduce function call overhead vs calling
     140              :  * bitbuffer_append_bit() in a loop. Used by COUNT encoding lookup table.
     141              :  *
     142              :  * Performance: ~2% compression speedup from this optimization. Combined with
     143              :  * DeBruijn bit extraction and word-level operations, achieves 14-16% faster
     144              :  * compression and 13-39% faster decompression on real-world datasets.
     145              :  *
     146              :  * @param[out] bb       Bit buffer
     147              :  * @param[in]  value    Value containing bits (right-justified)
     148              :  * @param[in]  num_bits Number of bits to append (1-24)
     149              :  * @return CCSDS124_OK on success, error code otherwise
     150              :  */
     151       250920 : int bitbuffer_append_value(bitbuffer_t *bb, uint32_t value, size_t num_bits) {
     152              :     int result = CCSDS124_ERROR_INVALID_ARG;
     153              : 
     154       250920 :     if ((bb != NULL) && (num_bits > 0U) && (num_bits <= 24U)) {
     155              :         /* Check for overflow */
     156              :         size_t max_bits = CCSDS124_MAX_OUTPUT_BYTES * 8U;
     157       250920 :         if ((bb->num_bits + num_bits) > max_bits) {
     158              :             result = CCSDS124_ERROR_OVERFLOW;
     159              :         } else {
     160              :             /* Add bits directly to accumulator.
     161              :              * Value is right-justified: the bottom 'num_bits' bits are the data.
     162              :              * Mask to ensure only the relevant bits are used. */
     163       250920 :             uint32_t mask = (1U << (uint32_t)num_bits) - 1U;
     164       250920 :             uint32_t masked_value = value & mask;
     165              : 
     166       250920 :             bb->acc = (bb->acc << (uint32_t)num_bits) | masked_value;
     167       250920 :             bb->acc_len += num_bits;
     168       250920 :             bb->num_bits += num_bits;
     169              : 
     170              :             /* Flush complete bytes */
     171              :             bitbuffer_flush_acc(bb);
     172              : 
     173              :             result = CCSDS124_OK;
     174              :         }
     175              :     }
     176              : 
     177       250920 :     return result;
     178              : }
     179              : 
     180              : 
     181         2591 : int bitbuffer_append_bitvector(bitbuffer_t *bb, const bitvector_t *bv) {
     182              :     int result = CCSDS124_ERROR_INVALID_ARG;
     183              : 
     184         2591 :     if ((bb != NULL) && (bv != NULL)) {
     185              :         result = CCSDS124_OK;
     186              : 
     187              :         /* Calculate number of bytes from bit length */
     188         2591 :         size_t num_bytes = (bv->length + 7U) / 8U;
     189              : 
     190              :         /* CCSDS MSB-first: bytes in order, but bits within each byte from MSB to LSB */
     191       157674 :         for (size_t byte_idx = 0U; (byte_idx < num_bytes) && (result == CCSDS124_OK); byte_idx++) {
     192              :             size_t bits_in_this_byte = 8U;
     193              : 
     194              :             /* Last byte may have fewer than 8 bits */
     195       155083 :             if (byte_idx == (num_bytes - 1U)) {
     196         2591 :                 size_t remainder = bv->length % 8U;
     197         2591 :                 if (remainder != 0U) {
     198              :                     bits_in_this_byte = remainder;
     199              :                 }
     200              :             }
     201              : 
     202              :             /* With MSB-first bitvector indexing: bit 0 is MSB, bit 7 is LSB
     203              :              * We want to append bits in order: MSB first, LSB last
     204              :              * So we iterate through positions 0, 1, 2, ..., bits_in_this_byte-1 */
     205       155083 :             size_t start_bit = byte_idx * 8U;
     206      1395740 :             for (size_t bit_offset = 0U; (bit_offset < bits_in_this_byte) && (result == CCSDS124_OK); bit_offset++) {
     207      1240657 :                 size_t pos = start_bit + bit_offset;
     208              :                 int bit = bitvector_get_bit(bv, pos);
     209              : 
     210      1240657 :                 result = bitbuffer_append_bit(bb, bit);
     211              :             }
     212              :         }
     213              :     }
     214              : 
     215         2591 :     return result;
     216              : }
     217              : 
     218              : /** @} */ /* End of Bit Appending Functions */
     219              : 
     220              : /**
     221              :  * @name Query Functions
     222              :  * @{
     223              :  */
     224              : 
     225              : 
     226           54 : size_t bitbuffer_size(const bitbuffer_t *bb) {
     227              :     size_t size = 0U;
     228              : 
     229           54 :     if (bb != NULL) {
     230           54 :         size = bb->num_bits;
     231              :     }
     232              : 
     233           54 :     return size;
     234              : }
     235              : 
     236              : /** @} */ /* End of Query Functions */
     237              : 
     238              : /**
     239              :  * @name Byte Conversion Functions
     240              :  * @{
     241              :  */
     242              : 
     243              : 
     244       189216 : size_t bitbuffer_to_bytes(const bitbuffer_t *bb, uint8_t *data, size_t max_bytes) {
     245              :     size_t num_bytes = 0U;
     246              : 
     247       189216 :     if ((bb != NULL) && (data != NULL)) {
     248              :         /* Calculate number of bytes needed (ceiling division) */
     249       189216 :         num_bytes = (bb->num_bits + 7U) / 8U;
     250              : 
     251       189216 :         if (num_bytes > max_bytes) {
     252              :             num_bytes = max_bytes;
     253              :         }
     254              : 
     255              :         /* Copy flushed bytes from data buffer */
     256       189216 :         size_t flushed_bytes = (bb->num_bits - bb->acc_len) / 8U;
     257       189216 :         if (flushed_bytes > num_bytes) {
     258              :             flushed_bytes = num_bytes;
     259              :         }
     260       189216 :         if (flushed_bytes > 0U) {
     261       187614 :             (void)memcpy(data, bb->data, flushed_bytes);
     262              :         }
     263              : 
     264              :         /* Handle remaining bits in accumulator */
     265       189216 :         if ((bb->acc_len > 0U) && (flushed_bytes < num_bytes)) {
     266              :             /* Shift accumulator bits to MSB position */
     267       169235 :             uint8_t last_byte = (uint8_t)(bb->acc << (8U - bb->acc_len));
     268       169235 :             data[flushed_bytes] = last_byte;
     269              :         }
     270              :     }
     271              : 
     272       189216 :     return num_bytes;
     273              : }
     274              : 
     275              : /** @} */ /* End of Byte Conversion Functions */
        

Generated by: LCOV version 2.0-1