Coverage Report

Created: 2025-07-16 07:53

/src/libavif/src/io.c
Line
Count
Source (jump to first uncovered line)
1
// Copyright 2020 Joe Drago. All rights reserved.
2
// SPDX-License-Identifier: BSD-2-Clause
3
4
#include "avif/internal.h"
5
6
#include <limits.h>
7
#include <stdio.h>
8
#include <string.h>
9
10
void avifIODestroy(avifIO * io)
11
34.5k
{
12
34.5k
    if (io && io->destroy) {
13
17.2k
        io->destroy(io);
14
17.2k
    }
15
34.5k
}
16
17
// --------------------------------------------------------------------------------------
18
// avifIOMemoryReader
19
20
typedef struct avifIOMemoryReader
21
{
22
    avifIO io; // this must be the first member for easy casting to avifIO*
23
    avifROData rodata;
24
} avifIOMemoryReader;
25
26
static avifResult avifIOMemoryReaderRead(struct avifIO * io, uint32_t readFlags, uint64_t offset, size_t size, avifROData * out)
27
104k
{
28
    // printf("avifIOMemoryReaderRead offset %" PRIu64 " size %zu\n", offset, size);
29
30
104k
    if (readFlags != 0) {
31
        // Unsupported readFlags
32
0
        return AVIF_RESULT_IO_ERROR;
33
0
    }
34
35
104k
    avifIOMemoryReader * reader = (avifIOMemoryReader *)io;
36
37
    // Sanitize/clamp incoming request
38
104k
    if (offset > reader->rodata.size) {
39
        // The offset is past the end of the buffer.
40
0
        return AVIF_RESULT_IO_ERROR;
41
0
    }
42
104k
    uint64_t availableSize = reader->rodata.size - offset;
43
104k
    if (size > availableSize) {
44
940
        size = (size_t)availableSize;
45
940
    }
46
47
    // Prevent the offset addition from triggering an undefined behavior
48
    // sanitizer error if data is NULL (happens even with offset zero).
49
104k
    out->data = offset ? reader->rodata.data + offset : reader->rodata.data;
50
104k
    out->size = size;
51
104k
    return AVIF_RESULT_OK;
52
104k
}
53
54
static void avifIOMemoryReaderDestroy(struct avifIO * io)
55
17.2k
{
56
17.2k
    avifFree(io);
57
17.2k
}
58
59
avifIO * avifIOCreateMemoryReader(const uint8_t * data, size_t size)
60
17.2k
{
61
17.2k
    avifIOMemoryReader * reader = (avifIOMemoryReader *)avifAlloc(sizeof(avifIOMemoryReader));
62
17.2k
    if (reader == NULL) {
63
0
        return NULL;
64
0
    }
65
17.2k
    memset(reader, 0, sizeof(avifIOMemoryReader));
66
17.2k
    reader->io.destroy = avifIOMemoryReaderDestroy;
67
17.2k
    reader->io.read = avifIOMemoryReaderRead;
68
17.2k
    reader->io.sizeHint = size;
69
17.2k
    reader->io.persistent = AVIF_TRUE;
70
17.2k
    reader->rodata.data = data;
71
17.2k
    reader->rodata.size = size;
72
17.2k
    return (avifIO *)reader;
73
17.2k
}
74
75
// --------------------------------------------------------------------------------------
76
// avifIOFileReader
77
78
typedef struct avifIOFileReader
79
{
80
    avifIO io; // this must be the first member for easy casting to avifIO*
81
    avifRWData buffer;
82
    FILE * f;
83
} avifIOFileReader;
84
85
static avifResult avifIOFileReaderRead(struct avifIO * io, uint32_t readFlags, uint64_t offset, size_t size, avifROData * out)
86
0
{
87
    // printf("avifIOFileReaderRead offset %" PRIu64 " size %zu\n", offset, size);
88
89
0
    if (readFlags != 0) {
90
        // Unsupported readFlags
91
0
        return AVIF_RESULT_IO_ERROR;
92
0
    }
93
94
0
    avifIOFileReader * reader = (avifIOFileReader *)io;
95
96
    // Sanitize/clamp incoming request
97
0
    if (offset > reader->io.sizeHint) {
98
        // The offset is past the EOF.
99
0
        return AVIF_RESULT_IO_ERROR;
100
0
    }
101
0
    uint64_t availableSize = reader->io.sizeHint - offset;
102
0
    if (size > availableSize) {
103
0
        size = (size_t)availableSize;
104
0
    }
105
106
0
    if (size > 0) {
107
0
        if (offset > LONG_MAX) {
108
0
            return AVIF_RESULT_IO_ERROR;
109
0
        }
110
0
        if (reader->buffer.size < size) {
111
0
            AVIF_CHECKRES(avifRWDataRealloc(&reader->buffer, size));
112
0
        }
113
0
        if (fseek(reader->f, (long)offset, SEEK_SET) != 0) {
114
0
            return AVIF_RESULT_IO_ERROR;
115
0
        }
116
0
        size_t bytesRead = fread(reader->buffer.data, 1, size, reader->f);
117
0
        if (size != bytesRead) {
118
0
            if (ferror(reader->f)) {
119
0
                return AVIF_RESULT_IO_ERROR;
120
0
            }
121
0
            size = bytesRead;
122
0
        }
123
0
    }
124
125
0
    out->data = reader->buffer.data;
126
0
    out->size = size;
127
0
    return AVIF_RESULT_OK;
128
0
}
129
130
static void avifIOFileReaderDestroy(struct avifIO * io)
131
0
{
132
0
    avifIOFileReader * reader = (avifIOFileReader *)io;
133
0
    fclose(reader->f);
134
0
    avifRWDataFree(&reader->buffer);
135
0
    avifFree(io);
136
0
}
137
138
avifIO * avifIOCreateFileReader(const char * filename)
139
0
{
140
0
    FILE * f = fopen(filename, "rb");
141
0
    if (!f) {
142
0
        return NULL;
143
0
    }
144
145
0
    fseek(f, 0, SEEK_END);
146
0
    long fileSize = ftell(f);
147
0
    if (fileSize < 0) {
148
0
        fclose(f);
149
0
        return NULL;
150
0
    }
151
0
    fseek(f, 0, SEEK_SET);
152
153
0
    avifIOFileReader * reader = (avifIOFileReader *)avifAlloc(sizeof(avifIOFileReader));
154
0
    if (!reader) {
155
0
        fclose(f);
156
0
        return NULL;
157
0
    }
158
0
    memset(reader, 0, sizeof(avifIOFileReader));
159
0
    reader->f = f;
160
0
    reader->io.destroy = avifIOFileReaderDestroy;
161
0
    reader->io.read = avifIOFileReaderRead;
162
0
    reader->io.sizeHint = (uint64_t)fileSize;
163
0
    reader->io.persistent = AVIF_FALSE;
164
0
    if (avifRWDataRealloc(&reader->buffer, 1024) != AVIF_RESULT_OK) {
165
0
        avifFree(reader);
166
0
        fclose(f);
167
0
        return NULL;
168
0
    }
169
0
    return (avifIO *)reader;
170
0
}