Coverage Report

Created: 2026-05-11 07:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libavif/src/io.c
Line
Count
Source
1
// Copyright 2020 Joe Drago. All rights reserved.
2
// SPDX-License-Identifier: BSD-2-Clause
3
4
#if !defined(_WIN32)
5
// Ensure off_t is 64 bits.
6
#undef _FILE_OFFSET_BITS
7
#define _FILE_OFFSET_BITS 64
8
// Ensure we have some POSIX compatibility with fseeko/ftello.
9
#undef _POSIX_C_SOURCE
10
#define _POSIX_C_SOURCE 200112L
11
#endif
12
13
#include "avif/internal.h"
14
15
#include <assert.h>
16
#include <limits.h>
17
#include <stdint.h>
18
#include <stdio.h>
19
#include <string.h>
20
21
#if defined(_WIN32)
22
// Windows uses _fseeki64 / _ftelli64 for large file support
23
typedef __int64 avif_off_t;
24
#define AVIF_OFF_MAX INT64_MAX
25
26
static int avif_fseeko(FILE * stream, avif_off_t offset, int whence)
27
{
28
    return _fseeki64(stream, offset, whence);
29
}
30
31
static avif_off_t avif_ftello(FILE * stream)
32
{
33
    return _ftelli64(stream);
34
}
35
#else
36
37
#include <unistd.h>
38
39
#if defined(__ANDROID__)
40
#include <android/api-level.h>
41
#if __ANDROID_API__ >= 24
42
#define AVIF_USE_FSEEKO
43
#endif
44
#elif defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112L
45
// Standard Modern POSIX. The _POSIX_VERSION >= 200112L test for fseeko/ftello
46
// is used in the first example in the APPLICATION USAGE section in
47
// https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/unistd.h.html.
48
#define AVIF_USE_FSEEKO
49
#endif
50
51
#if defined(AVIF_USE_FSEEKO)
52
// POSIX large file support
53
static_assert(sizeof(off_t) == sizeof(int64_t), "");
54
typedef off_t avif_off_t;
55
0
#define AVIF_OFF_MAX INT64_MAX
56
57
static int avif_fseeko(FILE * stream, avif_off_t offset, int whence)
58
0
{
59
0
    return fseeko(stream, offset, whence);
60
0
}
61
62
static avif_off_t avif_ftello(FILE * stream)
63
0
{
64
0
    return ftello(stream);
65
0
}
66
#else
67
// Unknown or very old platform. Fall back on fseek/ftell.
68
typedef long avif_off_t;
69
#define AVIF_OFF_MAX LONG_MAX
70
71
static int avif_fseeko(FILE * stream, avif_off_t offset, int whence)
72
{
73
    return fseek(stream, offset, whence);
74
}
75
76
static avif_off_t avif_ftello(FILE * stream)
77
{
78
    return ftell(stream);
79
}
80
#endif // defined(AVIF_USE_FSEEKO)
81
82
#endif // defined(_WIN32)
83
84
void avifIODestroy(avifIO * io)
85
215k
{
86
215k
    if (io && io->destroy) {
87
82.3k
        io->destroy(io);
88
82.3k
    }
89
215k
}
90
91
// --------------------------------------------------------------------------------------
92
// avifIOMemoryReader
93
94
typedef struct avifIOMemoryReader
95
{
96
    avifIO io; // this must be the first member for easy casting to avifIO*
97
    avifROData rodata;
98
} avifIOMemoryReader;
99
100
static avifResult avifIOMemoryReaderRead(struct avifIO * io, uint32_t readFlags, uint64_t offset, size_t size, avifROData * out)
101
545k
{
102
    // printf("avifIOMemoryReaderRead offset %" PRIu64 " size %zu\n", offset, size);
103
104
545k
    if (readFlags != 0) {
105
        // Unsupported readFlags
106
0
        return AVIF_RESULT_IO_ERROR;
107
0
    }
108
109
545k
    avifIOMemoryReader * reader = (avifIOMemoryReader *)io;
110
111
    // Sanitize/clamp incoming request
112
545k
    if (offset > reader->rodata.size) {
113
        // The offset is past the end of the buffer.
114
0
        return AVIF_RESULT_IO_ERROR;
115
0
    }
116
545k
    uint64_t availableSize = reader->rodata.size - offset;
117
545k
    if (size > availableSize) {
118
2.14k
        size = (size_t)availableSize;
119
2.14k
    }
120
121
    // Prevent the offset addition from triggering an undefined behavior
122
    // sanitizer error if data is NULL (happens even with offset zero).
123
545k
    out->data = offset ? reader->rodata.data + offset : reader->rodata.data;
124
545k
    out->size = size;
125
545k
    return AVIF_RESULT_OK;
126
545k
}
127
128
static void avifIOMemoryReaderDestroy(struct avifIO * io)
129
82.3k
{
130
82.3k
    avifFree(io);
131
82.3k
}
132
133
avifIO * avifIOCreateMemoryReader(const uint8_t * data, size_t size)
134
82.3k
{
135
82.3k
    avifIOMemoryReader * reader = (avifIOMemoryReader *)avifAlloc(sizeof(avifIOMemoryReader));
136
82.3k
    if (reader == NULL) {
137
0
        return NULL;
138
0
    }
139
82.3k
    memset(reader, 0, sizeof(avifIOMemoryReader));
140
82.3k
    reader->io.destroy = avifIOMemoryReaderDestroy;
141
82.3k
    reader->io.read = avifIOMemoryReaderRead;
142
82.3k
    reader->io.sizeHint = size;
143
82.3k
    reader->io.persistent = AVIF_TRUE;
144
82.3k
    reader->rodata.data = data;
145
82.3k
    reader->rodata.size = size;
146
82.3k
    return (avifIO *)reader;
147
82.3k
}
148
149
// --------------------------------------------------------------------------------------
150
// avifIOFileReader
151
152
typedef struct avifIOFileReader
153
{
154
    avifIO io; // this must be the first member for easy casting to avifIO*
155
    avifRWData buffer;
156
    FILE * f;
157
} avifIOFileReader;
158
159
static avifResult avifIOFileReaderRead(struct avifIO * io, uint32_t readFlags, uint64_t offset, size_t size, avifROData * out)
160
0
{
161
    // printf("avifIOFileReaderRead offset %" PRIu64 " size %zu\n", offset, size);
162
163
0
    if (readFlags != 0) {
164
        // Unsupported readFlags
165
0
        return AVIF_RESULT_IO_ERROR;
166
0
    }
167
168
0
    avifIOFileReader * reader = (avifIOFileReader *)io;
169
170
    // Sanitize/clamp incoming request
171
0
    if (offset > reader->io.sizeHint) {
172
        // The offset is past the EOF.
173
0
        return AVIF_RESULT_IO_ERROR;
174
0
    }
175
0
    uint64_t availableSize = reader->io.sizeHint - offset;
176
0
    if (size > availableSize) {
177
0
        size = (size_t)availableSize;
178
0
    }
179
180
0
    if (size > 0) {
181
0
        if (offset > AVIF_OFF_MAX) {
182
0
            return AVIF_RESULT_IO_ERROR;
183
0
        }
184
0
        if (reader->buffer.size < size) {
185
0
            AVIF_CHECKRES(avifRWDataRealloc(&reader->buffer, size));
186
0
        }
187
0
        if (avif_fseeko(reader->f, (avif_off_t)offset, SEEK_SET) != 0) {
188
0
            return AVIF_RESULT_IO_ERROR;
189
0
        }
190
0
        size_t bytesRead = fread(reader->buffer.data, 1, size, reader->f);
191
0
        if (size != bytesRead) {
192
0
            if (ferror(reader->f)) {
193
0
                return AVIF_RESULT_IO_ERROR;
194
0
            }
195
0
            size = bytesRead;
196
0
        }
197
0
    }
198
199
0
    out->data = reader->buffer.data;
200
0
    out->size = size;
201
0
    return AVIF_RESULT_OK;
202
0
}
203
204
static void avifIOFileReaderDestroy(struct avifIO * io)
205
0
{
206
0
    avifIOFileReader * reader = (avifIOFileReader *)io;
207
0
    fclose(reader->f);
208
0
    avifRWDataFree(&reader->buffer);
209
0
    avifFree(io);
210
0
}
211
212
avifIO * avifIOCreateFileReader(const char * filename)
213
0
{
214
0
    FILE * f = fopen(filename, "rb");
215
0
    if (!f) {
216
0
        return NULL;
217
0
    }
218
219
0
    if (avif_fseeko(f, 0, SEEK_END) != 0) {
220
0
        fclose(f);
221
0
        return NULL;
222
0
    }
223
0
    avif_off_t fileSize = avif_ftello(f);
224
0
    if (fileSize < 0) {
225
0
        fclose(f);
226
0
        return NULL;
227
0
    }
228
0
    if (avif_fseeko(f, 0, SEEK_SET) != 0) {
229
0
        fclose(f);
230
0
        return NULL;
231
0
    }
232
233
0
    avifIOFileReader * reader = (avifIOFileReader *)avifAlloc(sizeof(avifIOFileReader));
234
0
    if (!reader) {
235
0
        fclose(f);
236
0
        return NULL;
237
0
    }
238
0
    memset(reader, 0, sizeof(avifIOFileReader));
239
0
    reader->f = f;
240
0
    reader->io.destroy = avifIOFileReaderDestroy;
241
0
    reader->io.read = avifIOFileReaderRead;
242
0
    reader->io.sizeHint = (uint64_t)fileSize;
243
0
    reader->io.persistent = AVIF_FALSE;
244
0
    if (avifRWDataRealloc(&reader->buffer, 1024) != AVIF_RESULT_OK) {
245
0
        avifFree(reader);
246
0
        fclose(f);
247
0
        return NULL;
248
0
    }
249
0
    return (avifIO *)reader;
250
0
}