Coverage Report

Created: 2026-02-14 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libavif/src/stream.c
Line
Count
Source
1
// Copyright 2019 Joe Drago. All rights reserved.
2
// SPDX-License-Identifier: BSD-2-Clause
3
4
#include "avif/internal.h"
5
6
#include <assert.h>
7
#include <inttypes.h>
8
#include <stdlib.h>
9
#include <string.h>
10
11
// ---------------------------------------------------------------------------
12
// avifROStream
13
14
const uint8_t * avifROStreamCurrent(avifROStream * stream)
15
232k
{
16
232k
    return stream->raw->data + stream->offset;
17
232k
}
18
19
void avifROStreamStart(avifROStream * stream, avifROData * raw, avifDiagnostics * diag, const char * diagContext)
20
230k
{
21
230k
    stream->raw = raw;
22
230k
    stream->offset = 0;
23
230k
    stream->numUsedBitsInPartialByte = 0;
24
230k
    stream->diag = diag;
25
230k
    stream->diagContext = diagContext;
26
27
    // If diag is non-NULL, diagContext must also be non-NULL
28
230k
    assert(!stream->diag || stream->diagContext);
29
230k
}
30
31
avifBool avifROStreamHasBytesLeft(const avifROStream * stream, size_t byteCount)
32
1.34M
{
33
1.34M
    return byteCount <= (stream->raw->size - stream->offset);
34
1.34M
}
35
36
size_t avifROStreamRemainingBytes(const avifROStream * stream)
37
213k
{
38
213k
    return stream->raw->size - stream->offset;
39
213k
}
40
41
size_t avifROStreamOffset(const avifROStream * stream)
42
28.5k
{
43
28.5k
    return stream->offset;
44
28.5k
}
45
46
void avifROStreamSetOffset(avifROStream * stream, size_t offset)
47
0
{
48
0
    assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required.
49
0
    stream->offset = offset;
50
0
    if (stream->offset > stream->raw->size) {
51
0
        stream->offset = stream->raw->size;
52
0
    }
53
0
}
54
55
avifBool avifROStreamSkip(avifROStream * stream, size_t byteCount)
56
297k
{
57
297k
    assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required.
58
297k
    if (!avifROStreamHasBytesLeft(stream, byteCount)) {
59
94
        avifDiagnosticsPrintf(stream->diag, "%s: Failed to skip %zu bytes, truncated data?", stream->diagContext, byteCount);
60
94
        return AVIF_FALSE;
61
94
    }
62
297k
    stream->offset += byteCount;
63
297k
    return AVIF_TRUE;
64
297k
}
65
66
avifBool avifROStreamRead(avifROStream * stream, uint8_t * data, size_t size)
67
867k
{
68
867k
    assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required.
69
867k
    if (!avifROStreamHasBytesLeft(stream, size)) {
70
714
        avifDiagnosticsPrintf(stream->diag, "%s: Failed to read %zu bytes, truncated data?", stream->diagContext, size);
71
714
        return AVIF_FALSE;
72
714
    }
73
74
866k
    memcpy(data, stream->raw->data + stream->offset, size);
75
866k
    stream->offset += size;
76
866k
    return AVIF_TRUE;
77
867k
}
78
79
avifBool avifROStreamReadUX8(avifROStream * stream, uint64_t * v, uint64_t factor)
80
16.4M
{
81
16.4M
    assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required.
82
16.4M
    if (factor == 0) {
83
        // Don't read anything, just set to 0
84
16.4M
        *v = 0;
85
16.4M
    } else if (factor == 1) {
86
0
        uint8_t tmp;
87
0
        AVIF_CHECK(avifROStreamRead(stream, &tmp, 1));
88
0
        *v = tmp;
89
28.9k
    } else if (factor == 2) {
90
0
        uint16_t tmp;
91
0
        AVIF_CHECK(avifROStreamReadU16(stream, &tmp));
92
0
        *v = tmp;
93
28.9k
    } else if (factor == 4) {
94
28.6k
        uint32_t tmp;
95
28.6k
        AVIF_CHECK(avifROStreamReadU32(stream, &tmp));
96
28.5k
        *v = tmp;
97
28.5k
    } else if (factor == 8) {
98
387
        uint64_t tmp;
99
387
        AVIF_CHECK(avifROStreamReadU64(stream, &tmp));
100
351
        *v = tmp;
101
351
    } else {
102
        // Unsupported factor
103
0
        avifDiagnosticsPrintf(stream->diag, "%s: Failed to read UX8 value; Unsupported UX8 factor [%" PRIu64 "]", stream->diagContext, factor);
104
0
        return AVIF_FALSE;
105
0
    }
106
16.4M
    return AVIF_TRUE;
107
16.4M
}
108
109
avifBool avifROStreamReadU16(avifROStream * stream, uint16_t * v)
110
124k
{
111
124k
    assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required.
112
124k
    AVIF_CHECK(avifROStreamRead(stream, (uint8_t *)v, sizeof(uint16_t)));
113
123k
    *v = avifNTOHS(*v);
114
123k
    return AVIF_TRUE;
115
124k
}
116
117
avifBool avifROStreamReadU16Endianness(avifROStream * stream, uint16_t * v, avifBool littleEndian)
118
0
{
119
0
    assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required.
120
0
    AVIF_CHECK(avifROStreamRead(stream, (uint8_t *)v, sizeof(uint16_t)));
121
0
    *v = littleEndian ? avifCTOHS(*v) : avifNTOHS(*v);
122
0
    return AVIF_TRUE;
123
0
}
124
125
avifBool avifROStreamReadU32(avifROStream * stream, uint32_t * v)
126
319k
{
127
319k
    assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required.
128
319k
    AVIF_CHECK(avifROStreamRead(stream, (uint8_t *)v, sizeof(uint32_t)));
129
319k
    *v = avifNTOHL(*v);
130
319k
    return AVIF_TRUE;
131
319k
}
132
133
avifBool avifROStreamReadU32Endianness(avifROStream * stream, uint32_t * v, avifBool littleEndian)
134
0
{
135
0
    assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required.
136
0
    AVIF_CHECK(avifROStreamRead(stream, (uint8_t *)v, sizeof(uint32_t)));
137
0
    *v = littleEndian ? avifCTOHL(*v) : avifNTOHL(*v);
138
0
    return AVIF_TRUE;
139
0
}
140
141
avifBool avifROStreamReadU64(avifROStream * stream, uint64_t * v)
142
1.78k
{
143
1.78k
    assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required.
144
1.78k
    AVIF_CHECK(avifROStreamRead(stream, (uint8_t *)v, sizeof(uint64_t)));
145
1.73k
    *v = avifNTOH64(*v);
146
1.73k
    return AVIF_TRUE;
147
1.78k
}
148
149
avifBool avifROStreamSkipBits(avifROStream * stream, size_t bitCount)
150
0
{
151
0
    if (stream->numUsedBitsInPartialByte != 0) {
152
0
        assert(stream->numUsedBitsInPartialByte < 8);
153
0
        const size_t padding = AVIF_MIN(8 - stream->numUsedBitsInPartialByte, bitCount);
154
0
        stream->numUsedBitsInPartialByte = (stream->numUsedBitsInPartialByte + padding) % 8;
155
0
        bitCount -= padding;
156
0
        if (bitCount == 0) {
157
0
            return AVIF_TRUE;
158
0
        }
159
0
    }
160
0
    const size_t num_bytes = (bitCount + 7) / 8;
161
0
    AVIF_CHECK(avifROStreamSkip(stream, num_bytes));
162
0
    stream->numUsedBitsInPartialByte = bitCount % 8;
163
0
    return AVIF_TRUE;
164
0
}
165
166
avifBool avifROStreamReadBitsU8(avifROStream * stream, uint8_t * v, size_t bitCount)
167
183k
{
168
183k
    AVIF_CHECK(bitCount <= sizeof(*v) * 8);
169
183k
    uint32_t vU32;
170
183k
    AVIF_CHECK(avifROStreamReadBitsU32(stream, &vU32, bitCount));
171
183k
    *v = (uint8_t)vU32;
172
183k
    return AVIF_TRUE;
173
183k
}
174
175
avifBool avifROStreamReadBitsU16(avifROStream * stream, uint16_t * v, size_t bitCount)
176
298
{
177
298
    AVIF_CHECK(bitCount <= sizeof(*v) * 8);
178
298
    uint32_t vU32;
179
298
    AVIF_CHECK(avifROStreamReadBitsU32(stream, &vU32, bitCount));
180
294
    *v = (uint16_t)vU32;
181
294
    return AVIF_TRUE;
182
298
}
183
184
avifBool avifROStreamReadBitsU32(avifROStream * stream, uint32_t * v, size_t bitCount)
185
265k
{
186
265k
    AVIF_CHECK(bitCount <= sizeof(*v) * 8);
187
265k
    *v = 0;
188
530k
    while (bitCount) {
189
265k
        if (stream->numUsedBitsInPartialByte == 0) {
190
105k
            AVIF_CHECK(avifROStreamSkip(stream, sizeof(uint8_t))); // Book a new partial byte in the stream.
191
105k
        }
192
265k
        assert(stream->offset > 0);
193
265k
        const uint8_t * packedBits = stream->raw->data + stream->offset - 1;
194
195
265k
        const size_t numBits = AVIF_MIN(bitCount, 8 - stream->numUsedBitsInPartialByte);
196
265k
        stream->numUsedBitsInPartialByte += numBits;
197
265k
        bitCount -= numBits;
198
        // The stream bits are packed starting with the most significant bit of the first input byte.
199
        // This way, packed bits can be found in the same order in the bit stream.
200
265k
        const uint32_t bits = (*packedBits >> (8 - stream->numUsedBitsInPartialByte)) & ((1 << numBits) - 1);
201
        // The value bits are ordered from the most significant bit to the least significant bit.
202
        // In the case where avifROStreamReadBitsU32() is used to parse the unsigned integer value *v
203
        // over multiple aligned bytes, this order corresponds to big endianness.
204
265k
        *v |= bits << bitCount;
205
206
265k
        if (stream->numUsedBitsInPartialByte == 8) {
207
            // Start a new partial byte the next time a bit is needed.
208
105k
            stream->numUsedBitsInPartialByte = 0;
209
105k
        }
210
265k
    }
211
264k
    return AVIF_TRUE;
212
265k
}
213
214
avifBool avifROStreamReadString(avifROStream * stream, char * output, size_t outputSize)
215
25.9k
{
216
25.9k
    assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required.
217
218
    // Check for the presence of a null terminator in the stream.
219
25.9k
    size_t remainingBytes = avifROStreamRemainingBytes(stream);
220
25.9k
    const uint8_t * p = avifROStreamCurrent(stream);
221
25.9k
    avifBool foundNullTerminator = AVIF_FALSE;
222
132k
    for (size_t i = 0; i < remainingBytes; ++i) {
223
132k
        if (p[i] == 0) {
224
25.9k
            foundNullTerminator = AVIF_TRUE;
225
25.9k
            break;
226
25.9k
        }
227
132k
    }
228
25.9k
    if (!foundNullTerminator) {
229
21
        avifDiagnosticsPrintf(stream->diag, "%s: Failed to find a NULL terminator when reading a string", stream->diagContext);
230
21
        return AVIF_FALSE;
231
21
    }
232
233
25.9k
    const char * streamString = (const char *)p;
234
25.9k
    size_t stringLen = strlen(streamString);
235
25.9k
    stream->offset += stringLen + 1; // update the stream to have read the "whole string" in
236
237
25.9k
    if (output && outputSize) {
238
        // clamp to our output buffer
239
742
        if (stringLen >= outputSize) {
240
0
            stringLen = outputSize - 1;
241
0
        }
242
742
        memcpy(output, streamString, stringLen);
243
742
        output[stringLen] = 0;
244
742
    }
245
25.9k
    return AVIF_TRUE;
246
25.9k
}
247
248
avifBool avifROStreamReadBoxHeaderPartial(avifROStream * stream, avifBoxHeader * header, avifBool topLevel)
249
201k
{
250
    // Section 4.2.2 of ISO/IEC 14496-12.
251
201k
    size_t startOffset = stream->offset;
252
253
201k
    uint32_t smallSize;
254
201k
    AVIF_CHECK(avifROStreamReadU32(stream, &smallSize));   // unsigned int(32) size;
255
201k
    AVIF_CHECK(avifROStreamRead(stream, header->type, 4)); // unsigned int(32) type = boxtype;
256
257
201k
    uint64_t size = smallSize;
258
201k
    if (size == 1) {
259
1.24k
        AVIF_CHECK(avifROStreamReadU64(stream, &size)); // unsigned int(64) largesize;
260
1.24k
    }
261
262
201k
    if (!memcmp(header->type, "uuid", 4)) {
263
351
        AVIF_CHECK(avifROStreamRead(stream, header->usertype, 16)); // unsigned int(8) usertype[16] = extended_type;
264
201k
    } else {
265
201k
        memset(header->usertype, 0, sizeof(header->usertype));
266
201k
    }
267
268
201k
    size_t bytesRead = stream->offset - startOffset;
269
201k
    if (size == 0) {
270
        // Section 4.2.2 of ISO/IEC 14496-12.
271
        //   if size is 0, then this box shall be in a top-level box (i.e. not contained in another
272
        //   box), and be the last box in its 'file', and its payload extends to the end of that
273
        //   enclosing 'file'. This is normally only used for a MediaDataBox ('mdat').
274
465
        if (!topLevel) {
275
16
            avifDiagnosticsPrintf(stream->diag, "%s: Non-top-level box with size 0", stream->diagContext);
276
16
            return AVIF_FALSE;
277
16
        }
278
279
        // The given stream may be incomplete and there is no guarantee that sizeHint is available and accurate.
280
        // Otherwise size could be set to avifROStreamRemainingBytes(stream) + (stream->offset - startOffset) right now.
281
282
        // Wait for avifIOReadFunc() to return AVIF_RESULT_OK.
283
449
        header->isSizeZeroBox = AVIF_TRUE;
284
449
        header->size = 0;
285
449
        return AVIF_TRUE;
286
465
    }
287
288
200k
    if ((size < bytesRead) || ((size - bytesRead) > SIZE_MAX)) {
289
6
        avifDiagnosticsPrintf(stream->diag, "%s: Header size overflow check failure", stream->diagContext);
290
6
        return AVIF_FALSE;
291
6
    }
292
200k
    header->isSizeZeroBox = AVIF_FALSE;
293
200k
    header->size = (size_t)(size - bytesRead);
294
200k
    return AVIF_TRUE;
295
200k
}
296
297
avifBool avifROStreamReadBoxHeader(avifROStream * stream, avifBoxHeader * header)
298
147k
{
299
147k
    AVIF_CHECK(avifROStreamReadBoxHeaderPartial(stream, header, /*topLevel=*/AVIF_FALSE));
300
147k
    if (header->size > avifROStreamRemainingBytes(stream)) {
301
174
        avifDiagnosticsPrintf(stream->diag, "%s: Child box too large, possibly truncated data", stream->diagContext);
302
174
        return AVIF_FALSE;
303
174
    }
304
146k
    return AVIF_TRUE;
305
147k
}
306
307
avifBool avifROStreamReadVersionAndFlags(avifROStream * stream, uint8_t * version, uint32_t * flags)
308
96.7k
{
309
96.7k
    uint8_t versionAndFlags[4];
310
96.7k
    AVIF_CHECK(avifROStreamRead(stream, versionAndFlags, 4));
311
96.7k
    if (version) {
312
96.7k
        *version = versionAndFlags[0];
313
96.7k
    }
314
96.7k
    if (flags) {
315
37.9k
        *flags = (versionAndFlags[1] << 16) + (versionAndFlags[2] << 8) + (versionAndFlags[3] << 0);
316
37.9k
    }
317
96.7k
    return AVIF_TRUE;
318
96.7k
}
319
320
avifBool avifROStreamReadAndEnforceVersion(avifROStream * stream, uint8_t enforcedVersion, uint32_t * flags)
321
37.3k
{
322
37.3k
    uint8_t version;
323
37.3k
    AVIF_CHECK(avifROStreamReadVersionAndFlags(stream, &version, flags));
324
37.3k
    if (version != enforcedVersion) {
325
10
        avifDiagnosticsPrintf(stream->diag, "%s: Expecting box version %u, got version %u", stream->diagContext, enforcedVersion, version);
326
10
        return AVIF_FALSE;
327
10
    }
328
37.3k
    return AVIF_TRUE;
329
37.3k
}
330
331
// ---------------------------------------------------------------------------
332
// avifRWStream
333
334
0
#define AVIF_STREAM_BUFFER_INCREMENT (1024 * 1024)
335
static avifResult makeRoom(avifRWStream * stream, size_t size)
336
0
{
337
0
    size_t neededSize = stream->offset + size;
338
0
    size_t newSize = stream->raw->size;
339
0
    while (newSize < neededSize) {
340
0
        newSize += AVIF_STREAM_BUFFER_INCREMENT;
341
0
    }
342
0
    return avifRWDataRealloc(stream->raw, newSize);
343
0
}
344
345
void avifRWStreamStart(avifRWStream * stream, avifRWData * raw)
346
0
{
347
0
    stream->raw = raw;
348
0
    stream->offset = 0;
349
0
    stream->numUsedBitsInPartialByte = 0;
350
0
}
351
352
size_t avifRWStreamOffset(const avifRWStream * stream)
353
0
{
354
0
    return stream->offset;
355
0
}
356
357
void avifRWStreamSetOffset(avifRWStream * stream, size_t offset)
358
0
{
359
0
    stream->offset = offset;
360
0
    if (stream->offset > stream->raw->size) {
361
0
        stream->offset = stream->raw->size;
362
0
    }
363
0
}
364
365
void avifRWStreamFinishWrite(avifRWStream * stream)
366
0
{
367
0
    if (stream->raw->size != stream->offset) {
368
0
        if (stream->offset) {
369
0
            stream->raw->size = stream->offset;
370
0
        } else {
371
0
            avifRWDataFree(stream->raw);
372
0
        }
373
0
    }
374
0
}
375
376
avifResult avifRWStreamWrite(avifRWStream * stream, const void * data, size_t size)
377
0
{
378
0
    assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required.
379
0
    if (size) {
380
0
        AVIF_CHECKRES(makeRoom(stream, size));
381
0
        memcpy(stream->raw->data + stream->offset, data, size);
382
0
        stream->offset += size;
383
0
    }
384
0
    return AVIF_RESULT_OK;
385
0
}
386
387
avifResult avifRWStreamWriteChars(avifRWStream * stream, const char * chars, size_t size)
388
0
{
389
0
    return avifRWStreamWrite(stream, chars, size);
390
0
}
391
392
avifResult avifRWStreamWriteFullBox(avifRWStream * stream, const char * type, size_t contentSize, int version, uint32_t flags, avifBoxMarker * marker)
393
0
{
394
0
    assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required.
395
0
    if (marker) {
396
0
        *marker = stream->offset;
397
0
    }
398
0
    size_t headerSize = sizeof(uint32_t) + 4 /* size of type */;
399
0
    if (version != -1) {
400
0
        headerSize += 4;
401
0
    }
402
403
0
    AVIF_CHECKRES(makeRoom(stream, headerSize));
404
0
    memset(stream->raw->data + stream->offset, 0, headerSize);
405
0
    uint32_t noSize = avifHTONL((uint32_t)(headerSize + contentSize));
406
0
    memcpy(stream->raw->data + stream->offset, &noSize, sizeof(uint32_t));
407
0
    memcpy(stream->raw->data + stream->offset + 4, type, 4);
408
0
    if (version != -1) {
409
0
        stream->raw->data[stream->offset + 8] = (uint8_t)version;
410
0
        stream->raw->data[stream->offset + 9] = (uint8_t)((flags >> 16) & 0xff);
411
0
        stream->raw->data[stream->offset + 10] = (uint8_t)((flags >> 8) & 0xff);
412
0
        stream->raw->data[stream->offset + 11] = (uint8_t)((flags >> 0) & 0xff);
413
0
    }
414
0
    stream->offset += headerSize;
415
416
0
    return AVIF_RESULT_OK;
417
0
}
418
419
avifResult avifRWStreamWriteBox(avifRWStream * stream, const char * type, size_t contentSize, avifBoxMarker * marker)
420
0
{
421
0
    return avifRWStreamWriteFullBox(stream, type, contentSize, -1, 0, marker);
422
0
}
423
424
void avifRWStreamFinishBox(avifRWStream * stream, avifBoxMarker marker)
425
0
{
426
0
    assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required.
427
0
    uint32_t noSize = avifHTONL((uint32_t)(stream->offset - marker));
428
0
    memcpy(stream->raw->data + marker, &noSize, sizeof(uint32_t));
429
0
}
430
431
avifResult avifRWStreamWriteU8(avifRWStream * stream, uint8_t v)
432
0
{
433
0
    assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required.
434
0
    AVIF_CHECKRES(makeRoom(stream, 1));
435
0
    stream->raw->data[stream->offset] = v;
436
0
    stream->offset += 1;
437
0
    return AVIF_RESULT_OK;
438
0
}
439
440
avifResult avifRWStreamWriteU16(avifRWStream * stream, uint16_t v)
441
0
{
442
0
    assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required.
443
0
    const size_t size = sizeof(uint16_t);
444
0
    AVIF_CHECKRES(makeRoom(stream, size));
445
0
    v = avifHTONS(v);
446
0
    memcpy(stream->raw->data + stream->offset, &v, size);
447
0
    stream->offset += size;
448
0
    return AVIF_RESULT_OK;
449
0
}
450
451
avifResult avifRWStreamWriteU32(avifRWStream * stream, uint32_t v)
452
0
{
453
0
    assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required.
454
0
    const size_t size = sizeof(uint32_t);
455
0
    AVIF_CHECKRES(makeRoom(stream, size));
456
0
    v = avifHTONL(v);
457
0
    memcpy(stream->raw->data + stream->offset, &v, size);
458
0
    stream->offset += size;
459
0
    return AVIF_RESULT_OK;
460
0
}
461
462
avifResult avifRWStreamWriteU64(avifRWStream * stream, uint64_t v)
463
0
{
464
0
    assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required.
465
0
    const size_t size = sizeof(uint64_t);
466
0
    AVIF_CHECKRES(makeRoom(stream, size));
467
0
    v = avifHTON64(v);
468
0
    memcpy(stream->raw->data + stream->offset, &v, size);
469
0
    stream->offset += size;
470
0
    return AVIF_RESULT_OK;
471
0
}
472
473
avifResult avifRWStreamWriteZeros(avifRWStream * stream, size_t byteCount)
474
0
{
475
0
    assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required.
476
0
    AVIF_CHECKRES(makeRoom(stream, byteCount));
477
0
    memset(stream->raw->data + stream->offset, 0, byteCount);
478
0
    stream->offset += byteCount;
479
0
    return AVIF_RESULT_OK;
480
0
}
481
482
avifResult avifRWStreamWriteBits(avifRWStream * stream, uint32_t v, size_t bitCount)
483
0
{
484
0
    AVIF_CHECKERR(bitCount >= 32 || (v >> bitCount) == 0, AVIF_RESULT_INVALID_ARGUMENT);
485
0
    while (bitCount) {
486
0
        if (stream->numUsedBitsInPartialByte == 0) {
487
0
            AVIF_CHECKRES(makeRoom(stream, 1)); // Book a new partial byte in the stream.
488
0
            stream->raw->data[stream->offset] = 0;
489
0
            stream->offset += 1;
490
0
        }
491
0
        assert(stream->offset > 0);
492
0
        uint8_t * packedBits = stream->raw->data + stream->offset - 1;
493
494
0
        const size_t numBits = AVIF_MIN(bitCount, 8 - stream->numUsedBitsInPartialByte);
495
0
        stream->numUsedBitsInPartialByte += numBits;
496
0
        bitCount -= numBits;
497
        // Order the input bits from the most significant bit to the least significant bit.
498
        // In the case where avifRWStreamWriteBits() is used to write the unsigned integer value v
499
        // over multiple aligned bytes, this order corresponds to big endianness.
500
0
        const uint32_t bits = (v >> bitCount) & ((1 << numBits) - 1);
501
        // Pack bits starting with the most significant bit of the first output byte.
502
        // This way, packed bits can be found in the same order in the bit stream.
503
0
        *packedBits |= bits << (8 - stream->numUsedBitsInPartialByte);
504
505
0
        if (stream->numUsedBitsInPartialByte == 8) {
506
            // Start a new partial byte the next time a bit is needed.
507
0
            stream->numUsedBitsInPartialByte = 0;
508
0
        }
509
0
    }
510
0
    return AVIF_RESULT_OK;
511
0
}