Coverage Report

Created: 2026-03-06 06:33

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
40.5M
{
16
40.5M
    return stream->raw->data + stream->offset;
17
40.5M
}
18
19
void avifROStreamStart(avifROStream * stream, avifROData * raw, avifDiagnostics * diag, const char * diagContext)
20
40.1M
{
21
40.1M
    stream->raw = raw;
22
40.1M
    stream->offset = 0;
23
40.1M
    stream->numUsedBitsInPartialByte = 0;
24
40.1M
    stream->diag = diag;
25
40.1M
    stream->diagContext = diagContext;
26
27
    // If diag is non-NULL, diagContext must also be non-NULL
28
40.1M
    assert(!stream->diag || stream->diagContext);
29
40.1M
}
30
31
avifBool avifROStreamHasBytesLeft(const avifROStream * stream, size_t byteCount)
32
338M
{
33
338M
    return byteCount <= (stream->raw->size - stream->offset);
34
338M
}
35
36
size_t avifROStreamRemainingBytes(const avifROStream * stream)
37
44.5M
{
38
44.5M
    return stream->raw->size - stream->offset;
39
44.5M
}
40
41
size_t avifROStreamOffset(const avifROStream * stream)
42
3.23M
{
43
3.23M
    return stream->offset;
44
3.23M
}
45
46
void avifROStreamSetOffset(avifROStream * stream, size_t offset)
47
492
{
48
492
    assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required.
49
492
    stream->offset = offset;
50
492
    if (stream->offset > stream->raw->size) {
51
114
        stream->offset = stream->raw->size;
52
114
    }
53
492
}
54
55
avifBool avifROStreamSkip(avifROStream * stream, size_t byteCount)
56
74.2M
{
57
74.2M
    assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required.
58
74.2M
    if (!avifROStreamHasBytesLeft(stream, byteCount)) {
59
130
        avifDiagnosticsPrintf(stream->diag, "%s: Failed to skip %zu bytes, truncated data?", stream->diagContext, byteCount);
60
130
        return AVIF_FALSE;
61
130
    }
62
74.2M
    stream->offset += byteCount;
63
74.2M
    return AVIF_TRUE;
64
74.2M
}
65
66
avifBool avifROStreamRead(avifROStream * stream, uint8_t * data, size_t size)
67
240M
{
68
240M
    assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required.
69
240M
    if (!avifROStreamHasBytesLeft(stream, size)) {
70
1.76k
        avifDiagnosticsPrintf(stream->diag, "%s: Failed to read %zu bytes, truncated data?", stream->diagContext, size);
71
1.76k
        return AVIF_FALSE;
72
1.76k
    }
73
74
240M
    memcpy(data, stream->raw->data + stream->offset, size);
75
240M
    stream->offset += size;
76
240M
    return AVIF_TRUE;
77
240M
}
78
79
avifBool avifROStreamReadUX8(avifROStream * stream, uint64_t * v, uint64_t factor)
80
61.4M
{
81
61.4M
    assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required.
82
61.4M
    if (factor == 0) {
83
        // Don't read anything, just set to 0
84
43.8M
        *v = 0;
85
43.8M
    } else if (factor == 1) {
86
0
        uint8_t tmp;
87
0
        AVIF_CHECK(avifROStreamRead(stream, &tmp, 1));
88
0
        *v = tmp;
89
17.6M
    } else if (factor == 2) {
90
0
        uint16_t tmp;
91
0
        AVIF_CHECK(avifROStreamReadU16(stream, &tmp));
92
0
        *v = tmp;
93
17.6M
    } else if (factor == 4) {
94
17.6M
        uint32_t tmp;
95
17.6M
        AVIF_CHECK(avifROStreamReadU32(stream, &tmp));
96
17.6M
        *v = tmp;
97
17.6M
    } else if (factor == 8) {
98
1.38k
        uint64_t tmp;
99
1.38k
        AVIF_CHECK(avifROStreamReadU64(stream, &tmp));
100
1.25k
        *v = tmp;
101
1.25k
    } 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
61.4M
    return AVIF_TRUE;
107
61.4M
}
108
109
avifBool avifROStreamReadU16(avifROStream * stream, uint16_t * v)
110
73.5M
{
111
73.5M
    assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required.
112
73.5M
    AVIF_CHECK(avifROStreamRead(stream, (uint8_t *)v, sizeof(uint16_t)));
113
73.5M
    *v = avifNTOHS(*v);
114
73.5M
    return AVIF_TRUE;
115
73.5M
}
116
117
avifBool avifROStreamReadU16Endianness(avifROStream * stream, uint16_t * v, avifBool littleEndian)
118
23.7k
{
119
23.7k
    assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required.
120
23.7k
    AVIF_CHECK(avifROStreamRead(stream, (uint8_t *)v, sizeof(uint16_t)));
121
23.5k
    *v = littleEndian ? avifCTOHS(*v) : avifNTOHS(*v);
122
23.5k
    return AVIF_TRUE;
123
23.7k
}
124
125
avifBool avifROStreamReadU32(avifROStream * stream, uint32_t * v)
126
72.4M
{
127
72.4M
    assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required.
128
72.4M
    AVIF_CHECK(avifROStreamRead(stream, (uint8_t *)v, sizeof(uint32_t)));
129
72.4M
    *v = avifNTOHL(*v);
130
72.4M
    return AVIF_TRUE;
131
72.4M
}
132
133
avifBool avifROStreamReadU32Endianness(avifROStream * stream, uint32_t * v, avifBool littleEndian)
134
8.25k
{
135
8.25k
    assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required.
136
8.25k
    AVIF_CHECK(avifROStreamRead(stream, (uint8_t *)v, sizeof(uint32_t)));
137
8.16k
    *v = littleEndian ? avifCTOHL(*v) : avifNTOHL(*v);
138
8.16k
    return AVIF_TRUE;
139
8.25k
}
140
141
avifBool avifROStreamReadU64(avifROStream * stream, uint64_t * v)
142
245k
{
143
245k
    assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required.
144
245k
    AVIF_CHECK(avifROStreamRead(stream, (uint8_t *)v, sizeof(uint64_t)));
145
245k
    *v = avifNTOH64(*v);
146
245k
    return AVIF_TRUE;
147
245k
}
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
52.8M
{
168
52.8M
    AVIF_CHECK(bitCount <= sizeof(*v) * 8);
169
52.8M
    uint32_t vU32;
170
52.8M
    AVIF_CHECK(avifROStreamReadBitsU32(stream, &vU32, bitCount));
171
52.8M
    *v = (uint8_t)vU32;
172
52.8M
    return AVIF_TRUE;
173
52.8M
}
174
175
avifBool avifROStreamReadBitsU16(avifROStream * stream, uint16_t * v, size_t bitCount)
176
1.50k
{
177
1.50k
    AVIF_CHECK(bitCount <= sizeof(*v) * 8);
178
1.50k
    uint32_t vU32;
179
1.50k
    AVIF_CHECK(avifROStreamReadBitsU32(stream, &vU32, bitCount));
180
1.49k
    *v = (uint16_t)vU32;
181
1.49k
    return AVIF_TRUE;
182
1.50k
}
183
184
avifBool avifROStreamReadBitsU32(avifROStream * stream, uint32_t * v, size_t bitCount)
185
91.6M
{
186
91.6M
    AVIF_CHECK(bitCount <= sizeof(*v) * 8);
187
91.6M
    *v = 0;
188
183M
    while (bitCount) {
189
91.8M
        if (stream->numUsedBitsInPartialByte == 0) {
190
42.2M
            AVIF_CHECK(avifROStreamSkip(stream, sizeof(uint8_t))); // Book a new partial byte in the stream.
191
42.2M
        }
192
91.8M
        assert(stream->offset > 0);
193
91.8M
        const uint8_t * packedBits = stream->raw->data + stream->offset - 1;
194
195
91.8M
        const size_t numBits = AVIF_MIN(bitCount, 8 - stream->numUsedBitsInPartialByte);
196
91.8M
        stream->numUsedBitsInPartialByte += numBits;
197
91.8M
        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
91.8M
        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
91.8M
        *v |= bits << bitCount;
205
206
91.8M
        if (stream->numUsedBitsInPartialByte == 8) {
207
            // Start a new partial byte the next time a bit is needed.
208
42.2M
            stream->numUsedBitsInPartialByte = 0;
209
42.2M
        }
210
91.8M
    }
211
91.6M
    return AVIF_TRUE;
212
91.6M
}
213
214
avifBool avifROStreamReadString(avifROStream * stream, char * output, size_t outputSize)
215
10.6M
{
216
10.6M
    assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required.
217
218
    // Check for the presence of a null terminator in the stream.
219
10.6M
    size_t remainingBytes = avifROStreamRemainingBytes(stream);
220
10.6M
    const uint8_t * p = avifROStreamCurrent(stream);
221
10.6M
    avifBool foundNullTerminator = AVIF_FALSE;
222
75.1M
    for (size_t i = 0; i < remainingBytes; ++i) {
223
75.1M
        if (p[i] == 0) {
224
10.6M
            foundNullTerminator = AVIF_TRUE;
225
10.6M
            break;
226
10.6M
        }
227
75.1M
    }
228
10.6M
    if (!foundNullTerminator) {
229
39
        avifDiagnosticsPrintf(stream->diag, "%s: Failed to find a NULL terminator when reading a string", stream->diagContext);
230
39
        return AVIF_FALSE;
231
39
    }
232
233
10.6M
    const char * streamString = (const char *)p;
234
10.6M
    size_t stringLen = strlen(streamString);
235
10.6M
    stream->offset += stringLen + 1; // update the stream to have read the "whole string" in
236
237
10.6M
    if (output && outputSize) {
238
        // clamp to our output buffer
239
466k
        if (stringLen >= outputSize) {
240
2
            stringLen = outputSize - 1;
241
2
        }
242
466k
        memcpy(output, streamString, stringLen);
243
466k
        output[stringLen] = 0;
244
466k
    }
245
10.6M
    return AVIF_TRUE;
246
10.6M
}
247
248
avifBool avifROStreamReadBoxHeaderPartial(avifROStream * stream, avifBoxHeader * header, avifBool topLevel)
249
37.7M
{
250
    // Section 4.2.2 of ISO/IEC 14496-12.
251
37.7M
    size_t startOffset = stream->offset;
252
253
37.7M
    uint32_t smallSize;
254
37.7M
    AVIF_CHECK(avifROStreamReadU32(stream, &smallSize));   // unsigned int(32) size;
255
37.7M
    AVIF_CHECK(avifROStreamRead(stream, header->type, 4)); // unsigned int(32) type = boxtype;
256
257
37.7M
    uint64_t size = smallSize;
258
37.7M
    if (size == 1) {
259
1.03k
        AVIF_CHECK(avifROStreamReadU64(stream, &size)); // unsigned int(64) largesize;
260
1.03k
    }
261
262
37.7M
    if (!memcmp(header->type, "uuid", 4)) {
263
66.8k
        AVIF_CHECK(avifROStreamRead(stream, header->usertype, 16)); // unsigned int(8) usertype[16] = extended_type;
264
37.6M
    } else {
265
37.6M
        memset(header->usertype, 0, sizeof(header->usertype));
266
37.6M
    }
267
268
37.7M
    size_t bytesRead = stream->offset - startOffset;
269
37.7M
    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
18.3k
        if (!topLevel) {
275
75
            avifDiagnosticsPrintf(stream->diag, "%s: Non-top-level box with size 0", stream->diagContext);
276
75
            return AVIF_FALSE;
277
75
        }
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
18.2k
        header->isSizeZeroBox = AVIF_TRUE;
284
18.2k
        header->size = 0;
285
18.2k
        return AVIF_TRUE;
286
18.3k
    }
287
288
37.6M
    if ((size < bytesRead) || ((size - bytesRead) > SIZE_MAX)) {
289
15
        avifDiagnosticsPrintf(stream->diag, "%s: Header size overflow check failure", stream->diagContext);
290
15
        return AVIF_FALSE;
291
15
    }
292
37.6M
    header->isSizeZeroBox = AVIF_FALSE;
293
37.6M
    header->size = (size_t)(size - bytesRead);
294
37.6M
    return AVIF_TRUE;
295
37.6M
}
296
297
avifBool avifROStreamReadBoxHeader(avifROStream * stream, avifBoxHeader * header)
298
28.8M
{
299
28.8M
    AVIF_CHECK(avifROStreamReadBoxHeaderPartial(stream, header, /*topLevel=*/AVIF_FALSE));
300
28.8M
    if (header->size > avifROStreamRemainingBytes(stream)) {
301
832
        avifDiagnosticsPrintf(stream->diag, "%s: Child box too large, possibly truncated data", stream->diagContext);
302
832
        return AVIF_FALSE;
303
832
    }
304
28.8M
    return AVIF_TRUE;
305
28.8M
}
306
307
avifBool avifROStreamReadVersionAndFlags(avifROStream * stream, uint8_t * version, uint32_t * flags)
308
21.4M
{
309
21.4M
    uint8_t versionAndFlags[4];
310
21.4M
    AVIF_CHECK(avifROStreamRead(stream, versionAndFlags, 4));
311
21.4M
    if (version) {
312
20.8M
        *version = versionAndFlags[0];
313
20.8M
    }
314
21.4M
    if (flags) {
315
12.5M
        *flags = (versionAndFlags[1] << 16) + (versionAndFlags[2] << 8) + (versionAndFlags[3] << 0);
316
12.5M
    }
317
21.4M
    return AVIF_TRUE;
318
21.4M
}
319
320
avifBool avifROStreamReadAndEnforceVersion(avifROStream * stream, uint8_t enforcedVersion, uint32_t * flags)
321
6.82M
{
322
6.82M
    uint8_t version;
323
6.82M
    AVIF_CHECK(avifROStreamReadVersionAndFlags(stream, &version, flags));
324
6.82M
    if (version != enforcedVersion) {
325
54
        avifDiagnosticsPrintf(stream->diag, "%s: Expecting box version %u, got version %u", stream->diagContext, enforcedVersion, version);
326
54
        return AVIF_FALSE;
327
54
    }
328
6.82M
    return AVIF_TRUE;
329
6.82M
}
330
331
// ---------------------------------------------------------------------------
332
// avifRWStream
333
334
100k
#define AVIF_STREAM_BUFFER_INCREMENT (1024 * 1024)
335
static avifResult makeRoom(avifRWStream * stream, size_t size)
336
6.82M
{
337
6.82M
    AVIF_CHECKERR(size <= SIZE_MAX - stream->offset, AVIF_RESULT_OUT_OF_MEMORY);
338
6.82M
    size_t newSize = stream->offset + size;
339
6.82M
    if (newSize <= stream->raw->size) {
340
6.77M
        return AVIF_RESULT_OK;
341
6.77M
    }
342
    // Make newSize a multiple of AVIF_STREAM_BUFFER_INCREMENT.
343
50.3k
    size_t rem = newSize % AVIF_STREAM_BUFFER_INCREMENT;
344
50.3k
    size_t padding = (rem == 0) ? 0 : AVIF_STREAM_BUFFER_INCREMENT - rem;
345
50.3k
    AVIF_CHECKERR(newSize <= SIZE_MAX - padding, AVIF_RESULT_OUT_OF_MEMORY);
346
50.3k
    newSize += padding;
347
50.3k
    return avifRWDataRealloc(stream->raw, newSize);
348
50.3k
}
349
350
void avifRWStreamStart(avifRWStream * stream, avifRWData * raw)
351
324k
{
352
324k
    stream->raw = raw;
353
324k
    stream->offset = 0;
354
324k
    stream->numUsedBitsInPartialByte = 0;
355
324k
}
356
357
size_t avifRWStreamOffset(const avifRWStream * stream)
358
956k
{
359
956k
    return stream->offset;
360
956k
}
361
362
void avifRWStreamSetOffset(avifRWStream * stream, size_t offset)
363
249k
{
364
249k
    stream->offset = offset;
365
249k
    if (stream->offset > stream->raw->size) {
366
0
        stream->offset = stream->raw->size;
367
0
    }
368
249k
}
369
370
void avifRWStreamFinishWrite(avifRWStream * stream)
371
50.2k
{
372
50.2k
    if (stream->raw->size != stream->offset) {
373
50.2k
        if (stream->offset) {
374
50.2k
            stream->raw->size = stream->offset;
375
50.2k
        } else {
376
0
            avifRWDataFree(stream->raw);
377
0
        }
378
50.2k
    }
379
50.2k
}
380
381
avifResult avifRWStreamWrite(avifRWStream * stream, const void * data, size_t size)
382
1.05M
{
383
1.05M
    assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required.
384
1.05M
    if (size) {
385
1.04M
        AVIF_CHECKRES(makeRoom(stream, size));
386
1.04M
        memcpy(stream->raw->data + stream->offset, data, size);
387
1.04M
        stream->offset += size;
388
1.04M
    }
389
1.05M
    return AVIF_RESULT_OK;
390
1.05M
}
391
392
avifResult avifRWStreamWriteChars(avifRWStream * stream, const char * chars, size_t size)
393
569k
{
394
569k
    return avifRWStreamWrite(stream, chars, size);
395
569k
}
396
397
avifResult avifRWStreamWriteFullBox(avifRWStream * stream, const char * type, size_t contentSize, int version, uint32_t flags, avifBoxMarker * marker)
398
1.34M
{
399
1.34M
    assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required.
400
1.34M
    if (marker) {
401
1.27M
        *marker = stream->offset;
402
1.27M
    }
403
1.34M
    size_t headerSize = sizeof(uint32_t) + 4 /* size of type */;
404
1.34M
    if (version != -1) {
405
819k
        headerSize += 4;
406
819k
    }
407
408
1.34M
    AVIF_CHECKRES(makeRoom(stream, headerSize));
409
1.34M
    memset(stream->raw->data + stream->offset, 0, headerSize);
410
1.34M
    uint32_t noSize = avifHTONL((uint32_t)(headerSize + contentSize));
411
1.34M
    memcpy(stream->raw->data + stream->offset, &noSize, sizeof(uint32_t));
412
1.34M
    memcpy(stream->raw->data + stream->offset + 4, type, 4);
413
1.34M
    if (version != -1) {
414
819k
        stream->raw->data[stream->offset + 8] = (uint8_t)version;
415
819k
        stream->raw->data[stream->offset + 9] = (uint8_t)((flags >> 16) & 0xff);
416
819k
        stream->raw->data[stream->offset + 10] = (uint8_t)((flags >> 8) & 0xff);
417
819k
        stream->raw->data[stream->offset + 11] = (uint8_t)((flags >> 0) & 0xff);
418
819k
    }
419
1.34M
    stream->offset += headerSize;
420
421
1.34M
    return AVIF_RESULT_OK;
422
1.34M
}
423
424
avifResult avifRWStreamWriteBox(avifRWStream * stream, const char * type, size_t contentSize, avifBoxMarker * marker)
425
521k
{
426
521k
    return avifRWStreamWriteFullBox(stream, type, contentSize, -1, 0, marker);
427
521k
}
428
429
void avifRWStreamFinishBox(avifRWStream * stream, avifBoxMarker marker)
430
1.27M
{
431
1.27M
    assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required.
432
1.27M
    uint32_t noSize = avifHTONL((uint32_t)(stream->offset - marker));
433
1.27M
    memcpy(stream->raw->data + marker, &noSize, sizeof(uint32_t));
434
1.27M
}
435
436
avifResult avifRWStreamWriteU8(avifRWStream * stream, uint8_t v)
437
281k
{
438
281k
    assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required.
439
281k
    AVIF_CHECKRES(makeRoom(stream, 1));
440
281k
    stream->raw->data[stream->offset] = v;
441
281k
    stream->offset += 1;
442
281k
    return AVIF_RESULT_OK;
443
281k
}
444
445
avifResult avifRWStreamWriteU16(avifRWStream * stream, uint16_t v)
446
1.29M
{
447
1.29M
    assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required.
448
1.29M
    const size_t size = sizeof(uint16_t);
449
1.29M
    AVIF_CHECKRES(makeRoom(stream, size));
450
1.29M
    v = avifHTONS(v);
451
1.29M
    memcpy(stream->raw->data + stream->offset, &v, size);
452
1.29M
    stream->offset += size;
453
1.29M
    return AVIF_RESULT_OK;
454
1.29M
}
455
456
avifResult avifRWStreamWriteU32(avifRWStream * stream, uint32_t v)
457
1.24M
{
458
1.24M
    assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required.
459
1.24M
    const size_t size = sizeof(uint32_t);
460
1.24M
    AVIF_CHECKRES(makeRoom(stream, size));
461
1.24M
    v = avifHTONL(v);
462
1.24M
    memcpy(stream->raw->data + stream->offset, &v, size);
463
1.24M
    stream->offset += size;
464
1.24M
    return AVIF_RESULT_OK;
465
1.24M
}
466
467
avifResult avifRWStreamWriteU64(avifRWStream * stream, uint64_t v)
468
195k
{
469
195k
    assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required.
470
195k
    const size_t size = sizeof(uint64_t);
471
195k
    AVIF_CHECKRES(makeRoom(stream, size));
472
195k
    v = avifHTON64(v);
473
195k
    memcpy(stream->raw->data + stream->offset, &v, size);
474
195k
    stream->offset += size;
475
195k
    return AVIF_RESULT_OK;
476
195k
}
477
478
avifResult avifRWStreamWriteZeros(avifRWStream * stream, size_t byteCount)
479
189k
{
480
189k
    assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required.
481
189k
    AVIF_CHECKRES(makeRoom(stream, byteCount));
482
189k
    memset(stream->raw->data + stream->offset, 0, byteCount);
483
189k
    stream->offset += byteCount;
484
189k
    return AVIF_RESULT_OK;
485
189k
}
486
487
avifResult avifRWStreamWriteBits(avifRWStream * stream, uint32_t v, size_t bitCount)
488
2.34M
{
489
2.34M
    AVIF_CHECKERR(bitCount >= 32 || (v >> bitCount) == 0, AVIF_RESULT_INVALID_ARGUMENT);
490
4.98M
    while (bitCount) {
491
2.63M
        if (stream->numUsedBitsInPartialByte == 0) {
492
1.23M
            AVIF_CHECKRES(makeRoom(stream, 1)); // Book a new partial byte in the stream.
493
1.23M
            stream->raw->data[stream->offset] = 0;
494
1.23M
            stream->offset += 1;
495
1.23M
        }
496
2.63M
        assert(stream->offset > 0);
497
2.63M
        uint8_t * packedBits = stream->raw->data + stream->offset - 1;
498
499
2.63M
        const size_t numBits = AVIF_MIN(bitCount, 8 - stream->numUsedBitsInPartialByte);
500
2.63M
        stream->numUsedBitsInPartialByte += numBits;
501
2.63M
        bitCount -= numBits;
502
        // Order the input bits from the most significant bit to the least significant bit.
503
        // In the case where avifRWStreamWriteBits() is used to write the unsigned integer value v
504
        // over multiple aligned bytes, this order corresponds to big endianness.
505
2.63M
        const uint32_t bits = (v >> bitCount) & ((1 << numBits) - 1);
506
        // Pack bits starting with the most significant bit of the first output byte.
507
        // This way, packed bits can be found in the same order in the bit stream.
508
2.63M
        *packedBits |= bits << (8 - stream->numUsedBitsInPartialByte);
509
510
2.63M
        if (stream->numUsedBitsInPartialByte == 8) {
511
            // Start a new partial byte the next time a bit is needed.
512
1.23M
            stream->numUsedBitsInPartialByte = 0;
513
1.23M
        }
514
2.63M
    }
515
2.34M
    return AVIF_RESULT_OK;
516
2.34M
}