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 | } |