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

            Line data    Source code
       1              : /**
       2              :  * @file decompress.c
       3              :  * @brief CCSDS 124.0-B-1 decompression algorithm implementation.
       4              :  *
       5              :  * @cond INTERNAL
       6              :  * ============================================================================
       7              :  *  _____                                   ____
       8              :  * |_   _|_ _ _ __   __ _  __ _ _ __ __ _  / ___| _ __   __ _  ___ ___
       9              :  *   | |/ _` | '_ \ / _` |/ _` | '__/ _` | \___ \| '_ \ / _` |/ __/ _ \
      10              :  *   | | (_| | | | | (_| | (_| | | | (_| |  ___) | |_) | (_| | (_|  __/
      11              :  *   |_|\__,_|_| |_|\__,_|\__, |_|  \__,_| |____/| .__/ \__,_|\___\___|
      12              :  *                        |___/                  |_|
      13              :  * ============================================================================
      14              :  * @endcond
      15              :  *
      16              :  * Implements CCSDS 124.0-B-1 decompression (inverse of Section 5.3):
      17              :  * - Bit reader for parsing compressed packets
      18              :  * - COUNT and RLE decoding
      19              :  * - Packet decompression and mask reconstruction
      20              :  *
      21              :  * Code follows MISRA C:2012 guidelines.
      22              :  *
      23              :  * @authors Georges Labrèche <georges@tanagraspace.com> — https://georges.fyi
      24              :  * @authors Claude Code (Anthropic) <noreply@anthropic.com>
      25              :  *
      26              :  * @see https://ccsds.org/Pubs/124x0b1.pdf CCSDS 124.0-B-1 Standard
      27              :  */
      28              : 
      29              : #include "ccsds124.h"
      30              : #include <string.h>
      31              : 
      32              : /**
      33              :  * @name Internal Helper Functions
      34              :  * @{
      35              :  */
      36              : 
      37              : /**
      38              :  * @brief Extract positions of all set bits in a bitvector using word-level processing.
      39              :  *
      40              :  * Much faster than iterating bit-by-bit when the bitvector is sparse.
      41              :  * Uses __builtin_clz for O(1) MSB position finding.
      42              :  *
      43              :  * @param[in]  bv         Bitvector to scan
      44              :  * @param[out] positions  Array to store positions (must be at least hamming_weight elements)
      45              :  * @param[in]  max_pos    Maximum positions to extract
      46              :  * @return Number of positions extracted
      47              :  */
      48       291262 : static size_t bitvector_get_set_positions(
      49              :     const bitvector_t *bv,
      50              :     size_t *positions,
      51              :     size_t max_pos
      52              : ) {
      53              :     size_t count = 0U;
      54              : 
      55              :     /* Process words in forward order to get positions in ascending order */
      56      4777932 :     for (size_t word = 0U; (word < bv->num_words) && (count < max_pos); word++) {
      57      4486670 :         uint32_t word_data = bv->data[word];
      58              : 
      59     46078464 :         while ((word_data != 0U) && (count < max_pos)) {
      60              :             /* Find MSB position using count leading zeros */
      61     41591794 :             int clz = __builtin_clz(word_data);
      62     41591794 :             size_t bit_pos_in_word = (size_t)clz;
      63              : 
      64              :             /* Global position */
      65     41591794 :             size_t global_pos = (word * 32U) + bit_pos_in_word;
      66              : 
      67     41591794 :             if (global_pos < bv->length) {
      68     41591794 :                 positions[count] = global_pos;
      69     41591794 :                 count++;
      70              :             }
      71              : 
      72              :             /* Clear the MSB we just processed */
      73     41591794 :             word_data &= ~(1U << (31U - (uint32_t)clz));
      74              :         }
      75              :     }
      76              : 
      77       291262 :     return count;
      78              : }
      79              : 
      80              : /** @} */ /* End of Internal Helper Functions */
      81              : 
      82              : /**
      83              :  * @name Bit Reader Functions
      84              :  * @{
      85              :  */
      86              : 
      87              : 
      88         2001 : void bitreader_init(bitreader_t *reader, const uint8_t *data, size_t num_bits) {
      89         2001 :     if (reader != NULL) {
      90         2113 :         reader->data = data;
      91         2113 :         reader->num_bits = num_bits;
      92         2001 :         reader->bit_pos = 0U;
      93              :     }
      94         2001 : }
      95              : 
      96              : 
      97            7 : int bitreader_read_bit(bitreader_t *reader) {
      98              :     int result = -1;
      99              : 
     100     48682726 :     if ((reader != NULL) && (reader->bit_pos < reader->num_bits)) {
     101     49755444 :         size_t byte_index = reader->bit_pos / 8U;
     102     49755444 :         size_t bit_index = reader->bit_pos % 8U;
     103              : 
     104              :         /* MSB-first: bit 0 of byte is at position 7 */
     105     49755444 :         uint8_t shifted = reader->data[byte_index] >> (7U - bit_index);
     106     49755444 :         uint8_t masked = shifted & 1U;
     107     43063991 :         result = (int)masked;
     108       530000 :         reader->bit_pos++;
     109              :     }
     110              : 
     111            7 :     return result;
     112              : }
     113              : 
     114              : 
     115       689852 : uint32_t bitreader_read_bits(bitreader_t *reader, size_t num_bits) {
     116              :     uint32_t value = 0U;
     117              : 
     118       689852 :     if ((reader != NULL) && (num_bits <= 32U)) {
     119              :         /* Check sufficient bits remain before reading */
     120       689852 :         if (bitreader_remaining(reader) < num_bits) {
     121              :             return 0U;
     122              :         }
     123              : 
     124      4634318 :         for (size_t i = 0U; i < num_bits; i++) {
     125              :             int bit = bitreader_read_bit(reader);
     126              :             if (bit >= 0) {
     127      3944466 :                 value = (value << 1U) | ((uint32_t)bit & 1U);
     128              :             }
     129              :         }
     130              :     }
     131              : 
     132              :     return value;
     133              : }
     134              : 
     135              : 
     136            8 : size_t bitreader_position(const bitreader_t *reader) {
     137              :     size_t pos = 0U;
     138              : 
     139            8 :     if (reader != NULL) {
     140            8 :         pos = reader->bit_pos;
     141              :     }
     142              : 
     143            8 :     return pos;
     144              : }
     145              : 
     146              : 
     147            1 : size_t bitreader_remaining(const bitreader_t *reader) {
     148              :     size_t remaining = 0U;
     149              : 
     150      2607412 :     if ((reader != NULL) && (reader->bit_pos < reader->num_bits)) {
     151       902811 :         remaining = reader->num_bits - reader->bit_pos;
     152              :     }
     153              : 
     154            1 :     return remaining;
     155              : }
     156              : 
     157              : 
     158            2 : void bitreader_align_byte(bitreader_t *reader) {
     159            2 :     if (reader != NULL) {
     160       187420 :         size_t bit_offset = reader->bit_pos % 8U;
     161       187420 :         if (bit_offset != 0U) {
     162       167529 :             reader->bit_pos += (8U - bit_offset);
     163              :         }
     164              :     }
     165            2 : }
     166              : 
     167              : /** @} */ /* End of Bit Reader Functions */
     168              : 
     169              : /**
     170              :  * @name Decoding Functions
     171              :  * @{
     172              :  */
     173              : 
     174              : 
     175      1072724 : int ccsds124_count_decode(bitreader_t *reader, uint32_t *value) {
     176              :     int result = CCSDS124_ERROR_INVALID_ARG;
     177              : 
     178      1072724 :     if ((reader == NULL) || (value == NULL)) {
     179              :         return result;
     180              :     }
     181              : 
     182              :     if (bitreader_remaining(reader) == 0U) {
     183              :         return CCSDS124_ERROR_UNDERFLOW;
     184              :     }
     185              : 
     186              :     /* Read first bit */
     187              :     int bit0 = bitreader_read_bit(reader);
     188              : 
     189      1072720 :     if (bit0 == 0) {
     190              :         /* Case 1: '0' → value is 1 */
     191       377763 :         *value = 1U;
     192              :         result = CCSDS124_OK;
     193              :     } else {
     194              :         /* First bit is 1, read second bit */
     195              :         int bit1 = bitreader_read_bit(reader);
     196              : 
     197       694957 :         if (bit1 == 0) {
     198              :             /* Case 2: '10' → terminator (value 0) */
     199       194538 :             *value = 0U;
     200              :             result = CCSDS124_OK;
     201              :         } else {
     202              :             /* First two bits are 11, read third bit */
     203              :             int bit2 = bitreader_read_bit(reader);
     204              : 
     205       500419 :             if (bit2 == 0) {
     206              :                 /* Case 3: '110' + 5 bits → value + 2 */
     207       254954 :                 if (bitreader_remaining(reader) < 5U) {
     208              :                     return CCSDS124_ERROR_UNDERFLOW;
     209              :                 }
     210       254953 :                 uint32_t raw = bitreader_read_bits(reader, 5U);
     211       254953 :                 *value = raw + 2U;
     212              :                 result = CCSDS124_OK;
     213              :             } else {
     214              :                 /* Case 4: '111' + variable bits
     215              :                  * Need to find the size by counting zeros until a 1 */
     216              :                 size_t size = 0U;
     217              :                 int next_bit = 0;
     218              : 
     219              :                 /* Count zeros to determine field size */
     220              :                 do {
     221              :                     next_bit = bitreader_read_bit(reader);
     222              :                     if (next_bit < 0) {
     223              :                         return CCSDS124_ERROR_UNDERFLOW;
     224              :                     }
     225       684642 :                     size++;
     226       684642 :                 } while (next_bit == 0);
     227              : 
     228              :                 /* Size of value field is size + 5 */
     229       245463 :                 size_t value_bits = size + 5U;
     230              : 
     231              :                 /* Back up one bit since the '1' is part of the value */
     232       245463 :                 reader->bit_pos--;
     233              : 
     234              :                 /* Check sufficient bits remain for value field */
     235       245463 :                 if (bitreader_remaining(reader) < value_bits) {
     236              :                     return CCSDS124_ERROR_UNDERFLOW;
     237              :                 }
     238              : 
     239              :                 /* Read the value field */
     240       245461 :                 uint32_t raw = bitreader_read_bits(reader, value_bits);
     241       245461 :                 *value = raw + 2U;
     242              :                 result = CCSDS124_OK;
     243              :             }
     244              :         }
     245              :     }
     246              : 
     247              :     return result;
     248              : }
     249              : 
     250              : 
     251       194524 : int ccsds124_rle_decode(bitreader_t *reader, bitvector_t *result, size_t length) {
     252              :     int status = CCSDS124_ERROR_INVALID_ARG;
     253              : 
     254       194524 :     if ((reader == NULL) || (result == NULL)) {
     255              :         return status;
     256              :     }
     257              : 
     258              :     /* Initialize result to all zeros */
     259       194522 :     (void)bitvector_init(result, length);
     260       194522 :     bitvector_zero(result);
     261              : 
     262              :     /* Start from end of vector (matching RLE encoding which processes LSB to MSB) */
     263              :     size_t bit_position = length;
     264              : 
     265              :     /* Read COUNT values until terminator */
     266       194522 :     uint32_t delta = 0U;
     267       194522 :     status = ccsds124_count_decode(reader, &delta);
     268              : 
     269      1069963 :     while ((status == CCSDS124_OK) && (delta != 0U)) {
     270              :         /* Delta represents (count of zeros + 1) */
     271       875445 :         if (delta > bit_position) {
     272              :             /* Invalid: delta exceeds remaining positions (v1.6/v1.7/v1.8) */
     273              :             return CCSDS124_ERROR_OVERFLOW;
     274              :         }
     275       875441 :         bit_position -= delta;
     276              :         /* Set the bit at this position */
     277              :         bitvector_set_bit(result, bit_position, 1);
     278              : 
     279              :         /* Read next delta */
     280       875441 :         status = ccsds124_count_decode(reader, &delta);
     281              :     }
     282              : 
     283              :     return status;
     284              : }
     285              : 
     286              : 
     287       186739 : int ccsds124_bit_insert(bitreader_t *reader, bitvector_t *data, const bitvector_t *mask) {
     288              :     int status = CCSDS124_ERROR_INVALID_ARG;
     289              : 
     290       186739 :     if ((reader == NULL) || (data == NULL) || (mask == NULL)) {
     291              :         return status;
     292              :     }
     293              : 
     294       186736 :     if (data->length != mask->length) {
     295              :         return status;
     296              :     }
     297              : 
     298       186735 :     size_t hamming = bitvector_hamming_weight(mask);
     299              : 
     300       186735 :     if (hamming == 0U) {
     301              :         /* No bits to insert */
     302              :         return CCSDS124_OK;
     303              :     }
     304              : 
     305              :     /* Check sufficient bits remain before reading */
     306       186029 :     if (bitreader_remaining(reader) < hamming) {
     307              :         return CCSDS124_ERROR_UNDERFLOW;
     308              :     }
     309              : 
     310              :     /* Collect positions of '1' bits in mask using word-level processing */
     311              :     size_t positions[CCSDS124_MAX_PACKET_LENGTH];
     312       186029 :     size_t pos_count = bitvector_get_set_positions(mask, positions, hamming);
     313              : 
     314              :     /* Insert bits in reverse order (matching BE extraction) */
     315     41049350 :     for (size_t i = pos_count; i > 0U; i--) {
     316              :         int bit = bitreader_read_bit(reader);
     317              :         if (bit < 0) {
     318              :             return CCSDS124_ERROR_UNDERFLOW;
     319              :         }
     320     40863321 :         bitvector_set_bit(data, positions[i - 1U], bit);
     321              :     }
     322              : 
     323              :     status = CCSDS124_OK;
     324              :     return status;
     325              : }
     326              : 
     327              : /** @} */ /* End of Decoding Functions */
     328              : 
     329              : /**
     330              :  * @name Decompressor Initialization
     331              :  * @{
     332              :  */
     333              : 
     334              : 
     335          178 : int ccsds124_decompressor_init(
     336              :     ccsds124_decompressor_t *decomp,
     337              :     size_t F,
     338              :     const bitvector_t *initial_mask,
     339              :     uint8_t robustness
     340              : ) {
     341          178 :     if (decomp == NULL) {
     342              :         return CCSDS124_ERROR_INVALID_ARG;
     343              :     }
     344              : 
     345          177 :     if ((F == 0U) || (F > (size_t)CCSDS124_MAX_PACKET_LENGTH)) {
     346              :         return CCSDS124_ERROR_INVALID_ARG;
     347              :     }
     348              : 
     349          173 :     if (robustness > (uint8_t)CCSDS124_MAX_ROBUSTNESS) {
     350              :         return CCSDS124_ERROR_INVALID_ARG;
     351              :     }
     352              : 
     353              :     /* Store configuration */
     354          171 :     decomp->F = F;
     355          171 :     decomp->robustness = robustness;
     356              : 
     357              :     /* Initialize bit vectors */
     358          171 :     (void)bitvector_init(&decomp->mask, F);
     359          171 :     (void)bitvector_init(&decomp->initial_mask, F);
     360          171 :     (void)bitvector_init(&decomp->prev_output, F);
     361          171 :     (void)bitvector_init(&decomp->Xt, F);
     362              : 
     363              :     /* Set initial mask */
     364          171 :     if (initial_mask != NULL) {
     365            1 :         bitvector_copy(&decomp->initial_mask, initial_mask);
     366            1 :         bitvector_copy(&decomp->mask, initial_mask);
     367              :     } else {
     368          170 :         bitvector_zero(&decomp->initial_mask);
     369          170 :         bitvector_zero(&decomp->mask);
     370              :     }
     371              : 
     372              :     /* Initialize diagnostics */
     373          171 :     decomp->mask_inconsistent = 0U;
     374          171 :     decomp->count_f_mismatch = 0U;
     375              : 
     376              :     /* Initialize accuracy guarantee tracking */
     377          171 :     decomp->mask_synced = 0U;
     378          171 :     decomp->received_status_count = 0U;
     379          171 :     decomp->received_status_index = 0U;
     380          171 :     (void)memset(decomp->received_status_ring, 0, sizeof(decomp->received_status_ring));
     381              : 
     382              :     /* Reset state */
     383          171 :     ccsds124_decompressor_reset(decomp);
     384              : 
     385          171 :     return CCSDS124_OK;
     386              : }
     387              : 
     388              : 
     389          234 : void ccsds124_decompressor_reset(ccsds124_decompressor_t *decomp) {
     390          234 :     if (decomp != NULL) {
     391          234 :         decomp->t = 0U;
     392          234 :         bitvector_copy(&decomp->mask, &decomp->initial_mask);
     393          234 :         bitvector_zero(&decomp->prev_output);
     394          234 :         bitvector_zero(&decomp->Xt);
     395              : 
     396              :         /* Reset diagnostics */
     397          234 :         decomp->mask_inconsistent = 0U;
     398          234 :         decomp->count_f_mismatch = 0U;
     399              : 
     400              :         /* Reset accuracy guarantee tracking */
     401          234 :         decomp->mask_synced = 0U;
     402          234 :         decomp->received_status_count = 0U;
     403          234 :         decomp->received_status_index = 0U;
     404          234 :         (void)memset(decomp->received_status_ring, 0, sizeof(decomp->received_status_ring));
     405              :     }
     406          234 : }
     407              : 
     408              : 
     409           56 : int ccsds124_decompressor_notify_packet_loss(
     410              :     ccsds124_decompressor_t *decomp,
     411              :     uint32_t lost_count
     412              : ) {
     413           56 :     if (decomp == NULL) {
     414              :         return CCSDS124_ERROR_INVALID_ARG;
     415              :     }
     416              : 
     417           56 :     if (lost_count == 0U) {
     418              :         return CCSDS124_OK;  /* No loss, nothing to do */
     419              :     }
     420              : 
     421              :     /*
     422              :      * Advance the time index to account for lost packets.
     423              :      * This is critical for synchronization with the compressor's time index.
     424              :      *
     425              :      * Note: After packet loss, the next packet should ideally have:
     426              :      * - rt=1 (uncompressed) for full recovery, OR
     427              :      * - ft=1 (full mask) for mask recovery
     428              :      *
     429              :      * If the loss count <= R (minimum robustness), the next packet's Xt
     430              :      * window will contain all mask changes, allowing mask synchronization.
     431              :      * However, prev_output will be stale unless the next packet is uncompressed.
     432              :      */
     433           56 :     decomp->t += lost_count;
     434              : 
     435              :     /*
     436              :      * Mark prev_output as potentially invalid.
     437              :      * The next packet's data should be uncompressed (rt=1) for reliable recovery,
     438              :      * or we accept that prediction-based decompression may fail.
     439              :      *
     440              :      * Per CCSDS 124.0-B-1 Section 3.3.2: "The uncompressed flag, rt, which
     441              :      * shall be rt = 1 if t <= Rt" - this ensures the first R+1 packets are
     442              :      * always uncompressed, providing natural synchronization points.
     443              :      */
     444              : 
     445              :     /* Packet loss breaks mask synchronization */
     446           56 :     decomp->mask_synced = 0U;
     447              : 
     448              :     /* Record 0x02 (lost) status entries in the ring buffer */
     449          254 :     for (uint32_t i = 0U; i < lost_count; i++) {
     450          198 :         decomp->received_status_ring[decomp->received_status_index] = 0x02U;
     451          198 :         decomp->received_status_index = (decomp->received_status_index + 1U) % CCSDS124_MAX_VT_HISTORY;
     452          198 :         if (decomp->received_status_count < CCSDS124_MAX_VT_HISTORY) {
     453          194 :             decomp->received_status_count++;
     454              :         }
     455              :     }
     456              : 
     457              :     return CCSDS124_OK;
     458              : }
     459              : 
     460              : /** @} */ /* End of Decompressor Initialization */
     461              : 
     462              : /**
     463              :  * @name Packet Decompression
     464              :  * @{
     465              :  */
     466              : 
     467              : /**
     468              :  * @brief Internal flags extracted during decompression.
     469              :  *
     470              :  * Used by ccsds124_decompress_packet_checked() to make accuracy
     471              :  * guarantee decisions without re-parsing the bitstream.
     472              :  */
     473              : typedef struct {
     474              :     uint8_t Vt;  /**< Effective robustness (0-15) */
     475              :     uint8_t ft;  /**< Send mask flag (0 or 1) */
     476              :     uint8_t rt;  /**< Reference/uncompressed flag (0 or 1) */
     477              : } ccsds124_decompress_flags_t;
     478              : 
     479              : /**
     480              :  * @brief Internal decompression with optional flag extraction.
     481              :  *
     482              :  * Core decompression logic shared by both ccsds124_decompress_packet()
     483              :  * and ccsds124_decompress_packet_checked().
     484              :  *
     485              :  * @param[in,out] decomp Decompressor state
     486              :  * @param[in,out] reader Bit reader
     487              :  * @param[out]    output Decompressed output
     488              :  * @param[out]    flags  Optional extracted flags (NULL to skip)
     489              :  * @return CCSDS124_OK or negative error code
     490              :  */
     491       189432 : static int ccsds124_decompress_packet_internal(
     492              :     ccsds124_decompressor_t *decomp,
     493              :     bitreader_t *reader,
     494              :     bitvector_t *output,
     495              :     ccsds124_decompress_flags_t *flags
     496              : ) {
     497       189432 :     if ((decomp == NULL) || (reader == NULL) || (output == NULL)) {
     498              :         return CCSDS124_ERROR_INVALID_ARG;
     499              :     }
     500              : 
     501       189429 :     (void)bitvector_init(output, decomp->F);
     502              : 
     503              :     /* Copy previous output as prediction base */
     504       189429 :     bitvector_copy(output, &decomp->prev_output);
     505              : 
     506              :     /* Clear positive changes tracker */
     507       189429 :     bitvector_zero(&decomp->Xt);
     508              : 
     509              :     /* ====================================================================
     510              :      * Parse hₜ: Mask change information
     511              :      * hₜ = RLE(Xₜ) ∥ BIT₄(Vₜ) ∥ eₜ ∥ kₜ ∥ cₜ ∥ ḋₜ
     512              :      * ==================================================================== */
     513              : 
     514              :     /* Decode RLE(Xₜ) - mask changes */
     515              :     bitvector_t Xt;
     516       189429 :     int status = ccsds124_rle_decode(reader, &Xt, decomp->F);
     517       189429 :     if (status != CCSDS124_OK) {
     518              :         return status;
     519              :     }
     520              : 
     521              :     /* Read BIT₄(Vₜ) - effective robustness */
     522       189423 :     if (bitreader_remaining(reader) < 4U) {
     523              :         return CCSDS124_ERROR_UNDERFLOW;
     524              :     }
     525       189423 :     uint32_t vt_raw = bitreader_read_bits(reader, 4U);
     526       189423 :     uint8_t Vt = (uint8_t)(vt_raw & 0x0FU);
     527              : 
     528              :     /* Process eₜ, kₜ, cₜ if Vₜ > 0 and there are changes */
     529              :     int ct = 0;
     530       189423 :     size_t change_count = bitvector_hamming_weight(&Xt);
     531              : 
     532       189423 :     if ((Vt > 0U) && (change_count > 0U)) {
     533              :         /* Read eₜ */
     534              :         int et = bitreader_read_bit(reader);
     535              :         if (et < 0) {
     536            0 :             return CCSDS124_ERROR_UNDERFLOW;
     537              :         }
     538              : 
     539              :         /* Pre-extract positions of set bits in Xt (word-level, much faster than bit-by-bit) */
     540              :         size_t change_positions[CCSDS124_MAX_PACKET_LENGTH];
     541       105206 :         size_t num_changes = bitvector_get_set_positions(&Xt, change_positions, change_count);
     542              : 
     543       105206 :         if (et == 1) {
     544              :             /* Read kₜ - determines positive/negative updates */
     545              :             /* kₜ has one bit per change in Xt */
     546              :             uint8_t kt_bits[CCSDS124_MAX_PACKET_LENGTH];
     547              : 
     548              :             /* Read kt bits using pre-extracted positions */
     549        24231 :             if (bitreader_remaining(reader) < num_changes) {
     550            0 :                 return CCSDS124_ERROR_UNDERFLOW;
     551              :             }
     552       441286 :             for (size_t idx = 0U; idx < num_changes; idx++) {
     553              :                 int bit_val = bitreader_read_bit(reader);
     554              :                 if (bit_val < 0) {
     555              :                     return CCSDS124_ERROR_UNDERFLOW;
     556              :                 }
     557       487199 :                 kt_bits[idx] = (bit_val > 0) ? 1U : 0U;
     558              :             }
     559              : 
     560              :             /* Apply mask updates using pre-extracted positions */
     561       441286 :             for (size_t idx = 0U; idx < num_changes; idx++) {
     562       417055 :                 size_t pos = change_positions[idx];
     563              :                 /* kt=1 means positive update (mask becomes 0) */
     564              :                 /* kt=0 means negative update (mask becomes 1) */
     565       417055 :                 if (kt_bits[idx] != 0U) {
     566              :                     bitvector_set_bit(&decomp->mask, pos, 0);
     567              :                     bitvector_set_bit(&decomp->Xt, pos, 1);  /* Track positive change */
     568              :                 } else {
     569              :                     bitvector_set_bit(&decomp->mask, pos, 1);
     570              :                 }
     571              :             }
     572              : 
     573              :             /* Read cₜ */
     574              :             ct = bitreader_read_bit(reader);
     575              :             if (ct < 0) {
     576              :                 return CCSDS124_ERROR_UNDERFLOW;
     577              :             }
     578              :         } else {
     579              :             /* et = 0: all updates are negative (mask bits become 1) */
     580       392222 :             for (size_t idx = 0U; idx < num_changes; idx++) {
     581       311247 :                 bitvector_set_bit(&decomp->mask, change_positions[idx], 1);
     582              :             }
     583              :         }
     584        84217 :     } else if ((Vt == 0U) && (change_count > 0U)) {
     585              :         /* Vt = 0: toggle mask bits at change positions */
     586              :         /* Pre-extract positions of set bits in Xt */
     587              :         size_t change_positions[CCSDS124_MAX_PACKET_LENGTH];
     588           27 :         size_t num_changes = bitvector_get_set_positions(&Xt, change_positions, change_count);
     589              : 
     590          198 :         for (size_t idx = 0U; idx < num_changes; idx++) {
     591          171 :             size_t pos = change_positions[idx];
     592              :             int current_val = bitvector_get_bit(&decomp->mask, pos);
     593              :             int toggled = 0;
     594              :             if (current_val == 0) {
     595              :                 toggled = 1;
     596              :             }
     597          171 :             bitvector_set_bit(&decomp->mask, pos, toggled);
     598              :         }
     599              :     } else {
     600              :         /* No changes to apply (change_count == 0) */
     601              :     }
     602              : 
     603              :     /* Read ḋₜ */
     604              :     int dt = bitreader_read_bit(reader);
     605              :     if (dt < 0) {
     606              :         return CCSDS124_ERROR_UNDERFLOW;
     607              :     }
     608              : 
     609              :     /* ====================================================================
     610              :      * Parse qₜ: Optional full mask
     611              :      * ==================================================================== */
     612              : 
     613              :     int ft = 0;
     614              :     int rt = 0;
     615              : 
     616              :     /* Reset diagnostics flags */
     617       189423 :     decomp->mask_inconsistent = 0U;
     618       189423 :     decomp->count_f_mismatch = 0U;
     619              : 
     620              :     /* dt=1 means both ft=0 and rt=0 (optimization per CCSDS Eq. 13) */
     621              :     /* dt=0 means we need to read ft and rt from the stream */
     622              : 
     623       189423 :     if (dt == 0) {
     624              :         /* Read ft flag */
     625              :         ft = bitreader_read_bit(reader);
     626              :         if (ft < 0) {
     627              :             return CCSDS124_ERROR_UNDERFLOW;
     628              :         }
     629              : 
     630         5343 :         if (ft == 1) {
     631              :             /* Save delta-updated mask before full mask replacement (v1.11) */
     632              :             bitvector_t delta_mask;
     633         5085 :             bitvector_copy(&delta_mask, &decomp->mask);
     634              : 
     635              :             /* Full mask follows: decode RLE(M XOR (M<<)) */
     636              :             bitvector_t mask_diff;
     637         5085 :             status = ccsds124_rle_decode(reader, &mask_diff, decomp->F);
     638         5085 :             if (status != CCSDS124_OK) {
     639            0 :                 return status;
     640              :             }
     641              : 
     642              :             /* Reverse the horizontal XOR to get the actual mask.
     643              :              * HXOR encoding: HXOR[i] = M[i] XOR M[i+1], with HXOR[F-1] = M[F-1]
     644              :              * Reversal: start from LSB (position F-1) and work towards MSB (position 0)
     645              :              * M[F-1] = HXOR[F-1] (just copy)
     646              :              * M[i] = HXOR[i] XOR M[i+1] for i < F-1
     647              :              */
     648              : 
     649              :             /* Copy LSB bit directly (position F-1 in bitvector) */
     650         5085 :             int current = bitvector_get_bit(&mask_diff, decomp->F - 1U);
     651         5085 :             bitvector_set_bit(&decomp->mask, decomp->F - 1U, current);
     652              : 
     653              :             /* Process remaining bits from F-2 down to 0 */
     654      2474496 :             for (size_t i = decomp->F - 1U; i > 0U; i--) {
     655      2469411 :                 size_t pos = i - 1U;
     656              :                 int hxor_bit = bitvector_get_bit(&mask_diff, pos);
     657              :                 /* M[pos] = HXOR[pos] XOR M[pos+1] = HXOR[pos] XOR current */
     658      2469411 :                 current = hxor_bit ^ current;
     659      2469411 :                 bitvector_set_bit(&decomp->mask, pos, current);
     660              :             }
     661              : 
     662              :             /* Check delta/full mask consistency (v1.11) */
     663         5085 :             if (bitvector_equals(&delta_mask, &decomp->mask) == 0) {
     664            3 :                 decomp->mask_inconsistent = 1U;
     665              :             }
     666              : 
     667              :         }
     668              : 
     669              :         /* Read rt flag */
     670              :         rt = bitreader_read_bit(reader);
     671              :         if (rt < 0) {
     672              :             return CCSDS124_ERROR_UNDERFLOW;
     673              :         }
     674              :     }
     675              : 
     676              :     /* Populate flags if requested */
     677       189423 :     if (flags != NULL) {
     678           33 :         flags->Vt = Vt;
     679           33 :         flags->ft = (ft != 0) ? 1U : 0U;
     680           33 :         flags->rt = (rt != 0) ? 1U : 0U;
     681              :     }
     682              : 
     683       189413 :     if (rt == 1) {
     684              :         /* Full packet follows: COUNT(F) ∥ Iₜ */
     685         2688 :         uint32_t packet_length = 0U;
     686         2688 :         status = ccsds124_count_decode(reader, &packet_length);
     687         2688 :         if (status != CCSDS124_OK) {
     688            0 :             return status;
     689              :         }
     690              : 
     691              :         /* Check COUNT(F) against expected packet length (diagnostic flag).
     692              :          * A mismatch indicates corruption but we still attempt decompression
     693              :          * so the harness can decide whether to accept or reject. */
     694         2688 :         if (packet_length != (uint32_t)decomp->F) {
     695            0 :             decomp->count_f_mismatch = 1U;
     696              :         }
     697              : 
     698              :         /* Read full packet */
     699         2688 :         if (bitreader_remaining(reader) < decomp->F) {
     700              :             return CCSDS124_ERROR_UNDERFLOW;
     701              :         }
     702      1250960 :         for (size_t i = 0U; i < decomp->F; i++) {
     703              :             int bit = bitreader_read_bit(reader);
     704              :             if (bit < 0) {
     705              :                 return CCSDS124_ERROR_UNDERFLOW;
     706              :             }
     707      1248272 :             bitvector_set_bit(output, i, bit);
     708              :         }
     709              :     } else {
     710              :         /* Compressed: extract unpredictable bits */
     711              :         bitvector_t extraction_mask;
     712       186735 :         (void)bitvector_init(&extraction_mask, decomp->F);
     713              : 
     714       186735 :         if ((ct == 1) && (Vt > 0U)) {
     715              :             /* BE(Iₜ, (Xₜ OR Mₜ)) */
     716           28 :             bitvector_or(&extraction_mask, &decomp->mask, &decomp->Xt);
     717              :         } else {
     718              :             /* BE(Iₜ, Mₜ) */
     719       186707 :             bitvector_copy(&extraction_mask, &decomp->mask);
     720              :         }
     721              : 
     722              :         /* Insert unpredictable bits */
     723       186735 :         status = ccsds124_bit_insert(reader, output, &extraction_mask);
     724       186735 :         if (status != CCSDS124_OK) {
     725            0 :             return status;
     726              :         }
     727              :     }
     728              : 
     729              :     /* ====================================================================
     730              :      * Update state for next cycle
     731              :      * ==================================================================== */
     732              : 
     733       189423 :     bitvector_copy(&decomp->prev_output, output);
     734       189423 :     decomp->t++;
     735              : 
     736       189423 :     return CCSDS124_OK;
     737              : }
     738              : 
     739              : 
     740         1972 : int ccsds124_decompress_packet(
     741              :     ccsds124_decompressor_t *decomp,
     742              :     bitreader_t *reader,
     743              :     bitvector_t *output
     744              : ) {
     745       189395 :     return ccsds124_decompress_packet_internal(decomp, reader, output, NULL);
     746              : }
     747              : 
     748              : 
     749           41 : int ccsds124_decompress_packet_checked(
     750              :     ccsds124_decompressor_t *decomp,
     751              :     const uint8_t *data,
     752              :     size_t num_bits,
     753              :     bitvector_t *output,
     754              :     ccsds124_decompress_result_t *result
     755              : ) {
     756           41 :     if ((decomp == NULL) || (data == NULL) || (output == NULL)) {
     757              :         return CCSDS124_ERROR_INVALID_ARG;
     758              :     }
     759              : 
     760           38 :     if (num_bits == 0U) {
     761              :         return CCSDS124_ERROR_INVALID_ARG;
     762              :     }
     763              : 
     764              :     /* Save decompressor state before attempting decompression.
     765              :      * Per cross-validation v1.9: restore state if packet is invalid
     766              :      * to avoid propagating errors to subsequent packets. */
     767              :     ccsds124_decompressor_t saved_decomp;
     768              :     (void)memcpy(&saved_decomp, decomp, sizeof(*decomp));
     769              : 
     770              :     /* Create bit reader and decompress with flag extraction */
     771              :     bitreader_t reader;
     772              :     bitreader_init(&reader, data, num_bits);
     773              : 
     774              :     ccsds124_decompress_flags_t flags;
     775           37 :     int rc = ccsds124_decompress_packet_internal(decomp, &reader, output, &flags);
     776              : 
     777              :     /* Validate: only padding bits should remain (at most 7) (v1.10).
     778              :      * Reference packets (rt=1) are exempt: they are self-delimiting via
     779              :      * COUNT(F), and per the cross-validation rules a Received Packet
     780              :      * Length larger than the bits actually needed means the remainder is
     781              :      * simply ignored. */
     782           38 :     if ((rc == CCSDS124_OK) && (flags.rt == 0U) &&
     783              :         (bitreader_remaining(&reader) >= 8U)) {
     784              :         rc = CCSDS124_ERROR_OVERFLOW;
     785              :     }
     786              : 
     787              :     if (rc != CCSDS124_OK) {
     788              :         /* Decompression failed: restore state */
     789              :         (void)memcpy(decomp, &saved_decomp, sizeof(*decomp));
     790            5 :         decomp->mask_synced = 0U;
     791              : 
     792              :         /* Record 0x01 in status ring */
     793            5 :         decomp->received_status_ring[decomp->received_status_index] = 0x01U;
     794            5 :         decomp->received_status_index = (decomp->received_status_index + 1U) % CCSDS124_MAX_VT_HISTORY;
     795            5 :         if (decomp->received_status_count < CCSDS124_MAX_VT_HISTORY) {
     796            5 :             decomp->received_status_count++;
     797              :         }
     798              : 
     799            5 :         if (result != NULL) {
     800            4 :             result->status = 0x01U;
     801            4 :             result->Vt = 0U;
     802            4 :             result->ft = 0U;
     803            4 :             result->rt = 0U;
     804              :         }
     805            5 :         return rc;
     806              :     }
     807              : 
     808              :     /* Decompression succeeded — evaluate accuracy guarantee decision tree */
     809           32 :     uint8_t mask_inconsistent_detected = ((decomp->mask_synced != 0U) &&
     810           15 :                                           (decomp->mask_inconsistent != 0U)) ? 1U : 0U;
     811           32 :     uint8_t count_f_mismatch_detected = (decomp->count_f_mismatch != 0U) ? 1U : 0U;
     812              : 
     813              :     uint8_t guaranteed;
     814           32 :     if (mask_inconsistent_detected != 0U) {
     815              :         guaranteed = 0U;
     816           32 :     } else if (count_f_mismatch_detected != 0U) {
     817              :         guaranteed = 0U;
     818           32 :     } else if (flags.rt == 1U) {
     819              :         /* Reference packet: guaranteed if mask is synced or ft=1 resynchronizes */
     820           23 :         if ((decomp->mask_synced != 0U) || (flags.ft == 1U)) {
     821              :             guaranteed = 1U;
     822              :         } else {
     823              :             guaranteed = 0U;
     824              :         }
     825              :     } else {
     826              :         /* Non-reference packet: check preceding Vt received packets.
     827              :          * Skip lost packets (0x02) in the window. */
     828              :         guaranteed = 1U;
     829            9 :         if (flags.Vt > 0U) {
     830              :             size_t checked = 0U;
     831              :             /* Walk backwards through the ring buffer (before current entry) */
     832            8 :             size_t ring_walk = decomp->received_status_count;
     833            8 :             size_t idx = (decomp->received_status_index + CCSDS124_MAX_VT_HISTORY - 1U) % CCSDS124_MAX_VT_HISTORY;
     834              : 
     835           27 :             while ((checked < (size_t)flags.Vt) && (ring_walk > 0U)) {
     836           19 :                 uint8_t st = decomp->received_status_ring[idx];
     837           19 :                 if (st == 0x02U) {
     838              :                     /* Skip lost packets */
     839            1 :                     idx = (idx + CCSDS124_MAX_VT_HISTORY - 1U) % CCSDS124_MAX_VT_HISTORY;
     840            1 :                     ring_walk--;
     841            1 :                     continue;
     842              :                 }
     843           18 :                 if (st != 0x00U) {
     844              :                     guaranteed = 0U;
     845              :                     break;
     846              :                 }
     847           18 :                 checked++;
     848           18 :                 idx = (idx + CCSDS124_MAX_VT_HISTORY - 1U) % CCSDS124_MAX_VT_HISTORY;
     849           18 :                 ring_walk--;
     850              :             }
     851              :             /* If we checked fewer than Vt received packets because history
     852              :              * is too short, trust the guarantee (early packets are reliable). */
     853              :         }
     854              :     }
     855              : 
     856              :     /* Apply state decisions based on guarantee result */
     857              :     uint8_t out_status;
     858              :     int ret;
     859              : 
     860            8 :     if (guaranteed != 0U) {
     861              :         out_status = 0x00U;
     862              :         ret = CCSDS124_OK;
     863              : 
     864              :         /* ft=1 resynchronizes the mask */
     865           28 :         if (flags.ft == 1U) {
     866           14 :             decomp->mask_synced = 1U;
     867              :         }
     868            4 :     } else if (mask_inconsistent_detected != 0U) {
     869              :         /* Mask inconsistency while synced: restore state, clear sync */
     870              :         (void)memcpy(decomp, &saved_decomp, sizeof(*decomp));
     871            0 :         decomp->mask_synced = 0U;
     872              :         out_status = 0x01U;
     873              :         ret = CCSDS124_STATUS_UNGUARANTEED;
     874            4 :     } else if (count_f_mismatch_detected != 0U) {
     875              :         /* COUNT(F) mismatch: keep state (ft=1 still syncs mask) */
     876              :         out_status = 0x01U;
     877              :         ret = CCSDS124_STATUS_UNGUARANTEED;
     878            0 :         if (flags.ft == 1U) {
     879            0 :             decomp->mask_synced = 1U;
     880              :         }
     881              :     } else {
     882              :         /* Unguaranteed for other reasons: restore state, clear sync */
     883              :         (void)memcpy(decomp, &saved_decomp, sizeof(*decomp));
     884            4 :         decomp->mask_synced = 0U;
     885              :         out_status = 0x01U;
     886              :         ret = CCSDS124_STATUS_UNGUARANTEED;
     887              :     }
     888              : 
     889              :     /* Record status in ring buffer */
     890           32 :     decomp->received_status_ring[decomp->received_status_index] = out_status;
     891           32 :     decomp->received_status_index = (decomp->received_status_index + 1U) % CCSDS124_MAX_VT_HISTORY;
     892           32 :     if (decomp->received_status_count < CCSDS124_MAX_VT_HISTORY) {
     893           32 :         decomp->received_status_count++;
     894              :     }
     895              : 
     896              :     /* Populate result struct if requested */
     897           32 :     if (result != NULL) {
     898           24 :         result->status = out_status;
     899           24 :         result->Vt = flags.Vt;
     900           24 :         result->ft = flags.ft;
     901           24 :         result->rt = flags.rt;
     902              :     }
     903              : 
     904              :     return ret;
     905              : }
     906              : 
     907              : 
     908              : /**
     909              :  * @brief Skip COUNT values in an RLE sequence until the terminator.
     910              :  *
     911              :  * Reads and discards COUNT values from the reader until a terminator
     912              :  * (COUNT value 0) is found. Returns the hamming weight (number of
     913              :  * non-terminator values decoded) via output parameter.
     914              :  *
     915              :  * @param[in,out] reader         Bit reader
     916              :  * @param[out]    hamming_weight Number of non-terminator COUNTs decoded
     917              :  * @return CCSDS124_OK on success, error code on decode failure
     918              :  */
     919              : /**
     920              :  * @brief Skip an RLE-encoded sequence, tracking hamming weight and span.
     921              :  *
     922              :  * The span is the sum of all RLE deltas, which equals one plus the highest
     923              :  * '1' position encoded. An RLE sequence for a vector of length F must have
     924              :  * span <= F — a larger span means the h or q vector is inconsistent with F
     925              :  * (cross-validation rule v1.6).
     926              :  *
     927              :  * @param[in,out] reader         Bit reader
     928              :  * @param[out]    hamming_weight Number of non-terminator COUNTs decoded
     929              :  * @param[out]    span           Sum of decoded deltas (1 + highest position)
     930              :  * @return CCSDS124_OK on success, error code on decode failure
     931              :  */
     932           23 : static int skip_rle_sequence_span(bitreader_t *reader, uint32_t *hamming_weight,
     933              :                                   uint64_t *span) {
     934              :     uint32_t hw = 0U;
     935              :     uint64_t total = 0U;
     936           23 :     uint32_t count_val = 0U;
     937           23 :     int rc = ccsds124_count_decode(reader, &count_val);
     938              : 
     939           46 :     while ((rc == CCSDS124_OK) && (count_val != 0U)) {
     940           23 :         hw++;
     941           23 :         total += (uint64_t)count_val;
     942           23 :         rc = ccsds124_count_decode(reader, &count_val);
     943              :     }
     944              : 
     945           23 :     if (rc == CCSDS124_OK) {
     946           22 :         *hamming_weight = hw;
     947           22 :         *span = total;
     948              :     }
     949              : 
     950           23 :     return rc;
     951              : }
     952              : 
     953              : 
     954           17 : int ccsds124_discover_packet_length(
     955              :     const uint8_t *data,
     956              :     size_t num_bits,
     957              :     uint32_t *packet_length
     958              : ) {
     959           17 :     if ((data == NULL) || (packet_length == NULL)) {
     960              :         return CCSDS124_ERROR_INVALID_ARG;
     961              :     }
     962              : 
     963           15 :     if (num_bits == 0U) {
     964              :         return CCSDS124_ERROR_INVALID_ARG;
     965              :     }
     966              : 
     967              :     /* Default: not discoverable from this packet */
     968           14 :     *packet_length = 0U;
     969              : 
     970              :     bitreader_t reader;
     971              :     bitreader_init(&reader, data, num_bits);
     972              : 
     973              :     /* 1. Skip RLE(Xt) — self-delimiting, doesn't need F */
     974           14 :     uint32_t H_Xt = 0U;
     975           14 :     uint64_t Xt_span = 0U;
     976           14 :     if (skip_rle_sequence_span(&reader, &H_Xt, &Xt_span) != CCSDS124_OK) {
     977              :         return CCSDS124_OK;  /* Parse error — not discoverable */
     978              :     }
     979              : 
     980              :     /* 2. BIT4(Vt) — 4 bits */
     981           13 :     if (bitreader_remaining(&reader) < 4U) {
     982              :         return CCSDS124_OK;
     983              :     }
     984           13 :     uint32_t Vt = bitreader_read_bits(&reader, 4U);
     985              : 
     986              :     /* 3. If H(Xt) > 0 and Vt > 0: read et, then kt+ct only if et==1 */
     987           13 :     if ((H_Xt > 0U) && (Vt > 0U)) {
     988              :         int et = bitreader_read_bit(&reader);
     989              :         if (et < 0) {
     990              :             return CCSDS124_OK;
     991              :         }
     992            4 :         if (et == 1) {
     993              :             /* kt: H(Xt) bits — skip them */
     994            0 :             if (bitreader_remaining(&reader) < (size_t)H_Xt) {
     995              :                 return CCSDS124_OK;
     996              :             }
     997            0 :             for (uint32_t i = 0U; i < H_Xt; i++) {
     998              :                 (void)bitreader_read_bit(&reader);
     999              :             }
    1000              :             /* ct: 1 bit */
    1001              :             if (bitreader_read_bit(&reader) < 0) {
    1002              :                 return CCSDS124_OK;
    1003              :             }
    1004              :         }
    1005              :         /* et==0: all changes are negative — no kt or ct in bitstream */
    1006              :     }
    1007              :     /* If H(Xt) > 0 and Vt == 0: no et/kt/ct (toggle mode) */
    1008              : 
    1009              :     /* 4. dt — 1 bit */
    1010              :     int dt = bitreader_read_bit(&reader);
    1011              :     if (dt < 0) {
    1012              :         return CCSDS124_OK;
    1013              :     }
    1014              : 
    1015           13 :     if (dt == 1) {
    1016              :         /* dt=1 means ft=0 and rt=0 — can't discover F */
    1017              :         return CCSDS124_OK;
    1018              :     }
    1019              : 
    1020              :     /* 5. dt=0: read ft flag */
    1021              :     int ft = bitreader_read_bit(&reader);
    1022              :     if (ft < 0) {
    1023              :         return CCSDS124_OK;
    1024              :     }
    1025              : 
    1026           11 :     uint64_t mask_span = 0U;
    1027           11 :     if (ft == 1) {
    1028              :         /* Full mask follows as RLE — skip it */
    1029              :         uint32_t mask_hw = 0U;
    1030            9 :         if (skip_rle_sequence_span(&reader, &mask_hw, &mask_span) != CCSDS124_OK) {
    1031            0 :             return CCSDS124_OK;
    1032              :         }
    1033              :     }
    1034              : 
    1035              :     /* Read rt flag */
    1036              :     int rt = bitreader_read_bit(&reader);
    1037              :     if (rt < 0) {
    1038              :         return CCSDS124_OK;
    1039              :     }
    1040              : 
    1041           11 :     if (rt != 1) {
    1042              :         /* Not a reference packet — can't discover F */
    1043              :         return CCSDS124_OK;
    1044              :     }
    1045              : 
    1046              :     /* 6. rt=1: read COUNT(F) */
    1047           10 :     uint32_t discovered_F = 0U;
    1048           10 :     int rc = ccsds124_count_decode(&reader, &discovered_F);
    1049           10 :     if ((rc != CCSDS124_OK) || (discovered_F == 0U)) {
    1050              :         return CCSDS124_OK;
    1051              :     }
    1052              : 
    1053              :     /* Validity (cross-validation rule v1.6): the signaled length must be
    1054              :      * within the standard's range (1..65535), and the packet's own h and q
    1055              :      * vectors must be consistent with it — an RLE span exceeding F encodes
    1056              :      * positions beyond the packet, making the signaled length
    1057              :      * untrustworthy. */
    1058           10 :     if ((discovered_F > 65535U) ||
    1059           10 :         (Xt_span > (uint64_t)discovered_F) ||
    1060           10 :         (mask_span > (uint64_t)discovered_F)) {
    1061              :         return CCSDS124_OK;
    1062              :     }
    1063              : 
    1064              :     /* Truncated reference packet: the bitstream ran out after COUNT(F) but
    1065              :      * before the full I_t. Per the cross-validation rules the signaled
    1066              :      * length is still to be considered ("stored as the actual packet length
    1067              :      * if it is valid and was not known before"), but it is weaker evidence
    1068              :      * than a fully-validated reference packet — report it distinctly so
    1069              :      * callers can prefer a strict discovery elsewhere in the stream. */
    1070           10 :     if (bitreader_remaining(&reader) < (size_t)discovered_F) {
    1071            1 :         *packet_length = discovered_F;
    1072            1 :         return CCSDS124_STATUS_TRUNCATED_LENGTH;
    1073              :     }
    1074              : 
    1075              :     /* Excess bits after I_t are ignored: reference packets are
    1076              :      * self-delimiting via COUNT(F), and a Received Packet Length larger
    1077              :      * than the bits actually needed means the remainder is ignored. */
    1078            9 :     *packet_length = discovered_F;
    1079            9 :     return CCSDS124_OK;
    1080              : }
    1081              : 
    1082              : 
    1083           66 : int ccsds124_decompress(
    1084              :     ccsds124_decompressor_t *decomp,
    1085              :     const uint8_t *input_data,
    1086              :     size_t input_size,
    1087              :     uint8_t *output_buffer,
    1088              :     size_t output_buffer_size,
    1089              :     size_t *output_size
    1090              : ) {
    1091           66 :     if ((decomp == NULL) || (input_data == NULL) ||
    1092           63 :         (output_buffer == NULL) || (output_size == NULL)) {
    1093              :         return CCSDS124_ERROR_INVALID_ARG;
    1094              :     }
    1095              : 
    1096              :     /* Reset decompressor */
    1097           61 :     ccsds124_decompressor_reset(decomp);
    1098              : 
    1099              :     /* Initialize bit reader */
    1100              :     bitreader_t reader;
    1101           61 :     bitreader_init(&reader, input_data, input_size * 8U);
    1102              : 
    1103              :     /* Output packet size in bytes */
    1104           61 :     size_t packet_bytes = (decomp->F + 7U) / 8U;
    1105              :     size_t total_output = 0U;
    1106              : 
    1107              :     /* Decompress packets until input exhausted */
    1108           61 :     while (bitreader_remaining(&reader) > 0U) {
    1109              :         bitvector_t output;
    1110              :         int status = ccsds124_decompress_packet(decomp, &reader, &output);
    1111       187423 :         if (status != CCSDS124_OK) {
    1112            5 :             return status;
    1113              :         }
    1114              : 
    1115              :         /* Check output buffer space */
    1116       187421 :         if ((total_output + packet_bytes) > output_buffer_size) {
    1117              :             return CCSDS124_ERROR_OVERFLOW;
    1118              :         }
    1119              : 
    1120              :         /* Copy to output buffer */
    1121       187418 :         (void)bitvector_to_bytes(&output, &output_buffer[total_output], packet_bytes);
    1122              :         total_output += packet_bytes;
    1123              : 
    1124              :         /* Align to byte boundary for next packet */
    1125              :         bitreader_align_byte(&reader);
    1126              :     }
    1127              : 
    1128           56 :     *output_size = total_output;
    1129           56 :     return CCSDS124_OK;
    1130              : }
    1131              : 
    1132              : /** @} */ /* End of Packet Decompression */
        

Generated by: LCOV version 2.0-1