Branch data Line data Source code
1 : : /**
2 : : * @file bitstream_write.c
3 : : * @brief Bit-level and byte-level write operations, buffer append, bulk copy.
4 : : */
5 : :
6 : : #include "bitstream_internal.h"
7 : :
8 : : #include <stdlib.h>
9 : : #include <string.h>
10 : :
11 : : /* ── Internal helpers ────────────────────────────────────────────────── */
12 : :
13 : 219533 : static tp_result ensure_capacity(tp_bitstream_writer *w, uint64_t n_bits)
14 : : {
15 : 219533 : size_t needed_bytes = (size_t)((w->pos + n_bits + 7) / 8);
16 [ + + ]: 219533 : if (needed_bytes <= w->cap)
17 : 219347 : return TP_OK;
18 : :
19 : : size_t new_cap;
20 [ + + ]: 186 : if (w->growth > 0) {
21 : 118 : new_cap = w->cap + w->growth;
22 : : } else {
23 : 68 : new_cap = w->cap * 2;
24 : : }
25 [ + + ]: 192 : while (new_cap < needed_bytes)
26 [ + - ]: 6 : new_cap = w->growth > 0 ? new_cap + w->growth : new_cap * 2;
27 : :
28 : : /* Allocation failure paths are excluded from coverage (LCOV_EXCL). */
29 : 186 : uint8_t *new_buf = realloc(w->buf, new_cap);
30 : 186 : if (!new_buf) /* LCOV_EXCL_BR_LINE */
31 : : return TP_ERR_ALLOC; /* LCOV_EXCL_LINE */
32 : :
33 : 186 : memset(new_buf + w->cap, 0, new_cap - w->cap);
34 : 186 : w->buf = new_buf;
35 : 186 : w->cap = new_cap;
36 : 186 : return TP_OK;
37 : : }
38 : :
39 : 1397315 : static inline void write_bit_msb(uint8_t *buf, uint64_t bit_pos, uint8_t val)
40 : : {
41 : 1397315 : size_t byte_idx = (size_t)(bit_pos / 8);
42 : 1397315 : uint8_t bit_idx = (uint8_t)(7 - (bit_pos % 8));
43 [ + + ]: 1397315 : if (val)
44 : 523966 : buf[byte_idx] = (uint8_t)(buf[byte_idx] | (1u << bit_idx));
45 : : else
46 : 873349 : buf[byte_idx] = (uint8_t)(buf[byte_idx] & ~(1u << bit_idx));
47 : 1397315 : }
48 : :
49 : : /* ── Bit-level write ─────────────────────────────────────────────────── */
50 : :
51 : 219295 : tp_result tp_bs_write_bits(tp_bitstream_writer *w, uint64_t value, unsigned int n)
52 : : {
53 [ + + + + : 219295 : if (!w || n == 0 || n > 64)
+ + ]
54 : 14 : return TP_ERR_INVALID_PARAM;
55 : :
56 : 219281 : tp_result rc = ensure_capacity(w, n);
57 : 219281 : if (rc != TP_OK) /* LCOV_EXCL_BR_LINE */
58 : : return rc; /* LCOV_EXCL_LINE */
59 : :
60 [ + + ]: 1616498 : for (unsigned int i = 0; i < n; i++) {
61 : 1397217 : uint8_t bit = (uint8_t)((value >> (n - 1 - i)) & 1);
62 : 1397217 : write_bit_msb(w->buf, w->pos, bit);
63 : 1397217 : w->pos++;
64 : : }
65 : 219281 : return TP_OK;
66 : : }
67 : :
68 : 71 : tp_result tp_bs_write_bits_signed(tp_bitstream_writer *w, int64_t value, unsigned int n)
69 : : {
70 [ + + + + : 71 : if (!w || n == 0 || n > 64)
+ + ]
71 : 6 : return TP_ERR_INVALID_PARAM;
72 : :
73 : : /* Mask to n bits -- this truncates the sign extension so only the
74 : : * low n bits are written, exactly as they would appear in a two's
75 : : * complement representation. On read-back, tp_bs_read_bits_signed()
76 : : * will re-extend the MSB. */
77 : 65 : uint64_t raw = (uint64_t)value;
78 [ + + ]: 65 : if (n < 64)
79 : 61 : raw &= ((uint64_t)1 << n) - 1;
80 : :
81 : 65 : return tp_bs_write_bits(w, raw, n);
82 : : }
83 : :
84 : 36 : tp_result tp_bs_write_bit(tp_bitstream_writer *w, uint8_t value)
85 : : {
86 [ + + ]: 36 : if (!w)
87 : 2 : return TP_ERR_INVALID_PARAM;
88 : :
89 : 34 : tp_result rc = ensure_capacity(w, 1);
90 : 34 : if (rc != TP_OK) /* LCOV_EXCL_BR_LINE */
91 : : return rc; /* LCOV_EXCL_LINE */
92 : :
93 : 34 : write_bit_msb(w->buf, w->pos, value & 1);
94 : 34 : w->pos++;
95 : 34 : return TP_OK;
96 : : }
97 : :
98 : 369 : tp_result tp_bs_writer_align_to_byte(tp_bitstream_writer *w)
99 : : {
100 [ + + ]: 369 : if (!w)
101 : 2 : return TP_ERR_INVALID_PARAM;
102 : 367 : uint64_t rem = w->pos % 8;
103 [ + + ]: 367 : if (rem != 0) {
104 : 213 : uint8_t pad = (uint8_t)(8 - rem);
105 : 213 : tp_result rc = ensure_capacity(w, pad);
106 : 213 : if (rc != TP_OK) /* LCOV_EXCL_BR_LINE */
107 : : return rc; /* LCOV_EXCL_LINE */
108 : : /* Zero bits are already there from calloc/memset */
109 : 213 : w->pos += pad;
110 : : }
111 : 367 : return TP_OK;
112 : : }
113 : :
114 : : /* ── Byte-level write ────────────────────────────────────────────────── */
115 : :
116 : 3771 : tp_result tp_bs_write_u8(tp_bitstream_writer *w, uint8_t val)
117 : : {
118 : 3771 : return tp_bs_write_bits(w, val, 8);
119 : : }
120 : :
121 : 280 : tp_result tp_bs_write_u16(tp_bitstream_writer *w, uint16_t val)
122 : : {
123 : 280 : return tp_bs_write_bits(w, val, 16);
124 : : }
125 : :
126 : 1925 : tp_result tp_bs_write_u32(tp_bitstream_writer *w, uint32_t val)
127 : : {
128 : 1925 : return tp_bs_write_bits(w, val, 32);
129 : : }
130 : :
131 : 36 : tp_result tp_bs_write_u64(tp_bitstream_writer *w, uint64_t val)
132 : : {
133 : 36 : return tp_bs_write_bits(w, val, 64);
134 : : }
135 : :
136 : 99 : tp_result tp_bs_write_bytes(tp_bitstream_writer *w, const uint8_t *buf, size_t n)
137 : : {
138 [ + + + + : 99 : if (!w || (!buf && n > 0))
+ + ]
139 : 4 : return TP_ERR_INVALID_PARAM;
140 : :
141 [ + + ]: 1378 : for (size_t i = 0; i < n; i++) {
142 : 1283 : tp_result rc = tp_bs_write_u8(w, buf[i]);
143 : 1283 : if (rc != TP_OK) /* LCOV_EXCL_BR_LINE */
144 : : return rc; /* LCOV_EXCL_LINE */
145 : : }
146 : 95 : return TP_OK;
147 : : }
148 : :
149 : : /* ── Buffer append ───────────────────────────────────────────────────── */
150 : :
151 : 7 : tp_result tp_bs_writer_append_buffer(tp_bitstream_writer *w, const uint8_t *buf, uint64_t bit_len)
152 : : {
153 [ + + + + : 7 : if (!w || (!buf && bit_len > 0))
+ + ]
154 : 4 : return TP_ERR_INVALID_PARAM;
155 : :
156 : 3 : tp_result rc = ensure_capacity(w, bit_len);
157 : 3 : if (rc != TP_OK) /* LCOV_EXCL_BR_LINE */
158 : : return rc; /* LCOV_EXCL_LINE */
159 : :
160 [ + + ]: 23 : for (uint64_t i = 0; i < bit_len; i++) {
161 : 20 : size_t byte_idx = (size_t)(i / 8);
162 : 20 : uint8_t bit_idx = (uint8_t)(7 - (i % 8));
163 : 20 : uint8_t bit = (uint8_t)((buf[byte_idx] >> bit_idx) & 1);
164 : 20 : write_bit_msb(w->buf, w->pos, bit);
165 : 20 : w->pos++;
166 : : }
167 : 3 : return TP_OK;
168 : : }
169 : :
170 : : /* ── Bulk copy ───────────────────────────────────────────────────────── */
171 : :
172 : 6 : tp_result tp_bs_copy_bits(tp_bitstream_reader *r, tp_bitstream_writer *w, uint64_t n_bits)
173 : : {
174 [ + + + + ]: 6 : if (!r || !w)
175 : 3 : return TP_ERR_INVALID_PARAM;
176 [ + + ]: 3 : if (r->pos + n_bits > r->bit_len)
177 : 1 : return TP_ERR_EOF;
178 : :
179 : 2 : tp_result rc = ensure_capacity(w, n_bits);
180 : 2 : if (rc != TP_OK) /* LCOV_EXCL_BR_LINE */
181 : : return rc; /* LCOV_EXCL_LINE */
182 : :
183 [ + + ]: 46 : for (uint64_t i = 0; i < n_bits; i++) {
184 : 44 : size_t byte_idx = (size_t)(r->pos / 8);
185 : 44 : uint8_t bit_idx = (uint8_t)(7 - (r->pos % 8));
186 : 44 : uint8_t bit = (uint8_t)((r->buf[byte_idx] >> bit_idx) & 1);
187 : 44 : write_bit_msb(w->buf, w->pos, bit);
188 : 44 : r->pos++;
189 : 44 : w->pos++;
190 : : }
191 : 2 : return TP_OK;
192 : : }
|