| // Copyright 2020 Joe Drago. All rights reserved. | |
| // SPDX-License-Identifier: BSD-2-Clause | |
| #if !defined(_WIN32) | |
| // Ensure off_t is 64 bits. | |
| #undef _FILE_OFFSET_BITS | |
| #define _FILE_OFFSET_BITS 64 | |
| // Ensure we have some POSIX compatibility with fseeko/ftello. | |
| #undef _POSIX_C_SOURCE | |
| #define _POSIX_C_SOURCE 200112L | |
| #endif | |
| #include "avif/internal.h" | |
| #include <assert.h> | |
| #include <limits.h> | |
| #include <stdint.h> | |
| #include <stdio.h> | |
| #include <string.h> | |
| #if defined(_WIN32) | |
| // Windows uses _fseeki64 / _ftelli64 for large file support | |
| typedef __int64 avif_off_t; | |
| #define AVIF_OFF_MAX INT64_MAX | |
| static int avif_fseeko(FILE * stream, avif_off_t offset, int whence) | |
| { | |
| return _fseeki64(stream, offset, whence); | |
| } | |
| static avif_off_t avif_ftello(FILE * stream) | |
| { | |
| return _ftelli64(stream); | |
| } | |
| #else | |
| #include <unistd.h> | |
| #if defined(__ANDROID__) | |
| #include <android/api-level.h> | |
| #if __ANDROID_API__ >= 24 | |
| #define AVIF_USE_FSEEKO | |
| #endif | |
| #elif defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112L | |
| // Standard Modern POSIX. The _POSIX_VERSION >= 200112L test for fseeko/ftello | |
| // is used in the first example in the APPLICATION USAGE section in | |
| // https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/unistd.h.html. | |
| #define AVIF_USE_FSEEKO | |
| #endif | |
| #if defined(AVIF_USE_FSEEKO) | |
| // POSIX large file support | |
| static_assert(sizeof(off_t) == sizeof(int64_t), ""); | |
| typedef off_t avif_off_t; | |
| #define AVIF_OFF_MAX INT64_MAX | |
| static int avif_fseeko(FILE * stream, avif_off_t offset, int whence) | |
| { | |
| return fseeko(stream, offset, whence); | |
| } | |
| static avif_off_t avif_ftello(FILE * stream) | |
| { | |
| return ftello(stream); | |
| } | |
| #else | |
| // Unknown or very old platform. Fall back on fseek/ftell. | |
| typedef long avif_off_t; | |
| #define AVIF_OFF_MAX LONG_MAX | |
| static int avif_fseeko(FILE * stream, avif_off_t offset, int whence) | |
| { | |
| return fseek(stream, offset, whence); | |
| } | |
| static avif_off_t avif_ftello(FILE * stream) | |
| { | |
| return ftell(stream); | |
| } | |
| #endif // defined(AVIF_USE_FSEEKO) | |
| #endif // defined(_WIN32) | |
| void avifIODestroy(avifIO * io) | |
| { | |
| if (io && io->destroy) { | |
| io->destroy(io); | |
| } | |
| } | |
| // -------------------------------------------------------------------------------------- | |
| // avifIOMemoryReader | |
| typedef struct avifIOMemoryReader | |
| { | |
| avifIO io; // this must be the first member for easy casting to avifIO* | |
| avifROData rodata; | |
| } avifIOMemoryReader; | |
| static avifResult avifIOMemoryReaderRead(struct avifIO * io, uint32_t readFlags, uint64_t offset, size_t size, avifROData * out) | |
| { | |
| // printf("avifIOMemoryReaderRead offset %" PRIu64 " size %zu\n", offset, size); | |
| if (readFlags != 0) { | |
| // Unsupported readFlags | |
| return AVIF_RESULT_IO_ERROR; | |
| } | |
| avifIOMemoryReader * reader = (avifIOMemoryReader *)io; | |
| // Sanitize/clamp incoming request | |
| if (offset > reader->rodata.size) { | |
| // The offset is past the end of the buffer. | |
| return AVIF_RESULT_IO_ERROR; | |
| } | |
| uint64_t availableSize = reader->rodata.size - offset; | |
| if (size > availableSize) { | |
| size = (size_t)availableSize; | |
| } | |
| // Prevent the offset addition from triggering an undefined behavior | |
| // sanitizer error if data is NULL (happens even with offset zero). | |
| out->data = offset ? reader->rodata.data + offset : reader->rodata.data; | |
| out->size = size; | |
| return AVIF_RESULT_OK; | |
| } | |
| static void avifIOMemoryReaderDestroy(struct avifIO * io) | |
| { | |
| avifFree(io); | |
| } | |
| avifIO * avifIOCreateMemoryReader(const uint8_t * data, size_t size) | |
| { | |
| avifIOMemoryReader * reader = (avifIOMemoryReader *)avifCalloc(1, sizeof(avifIOMemoryReader)); | |
| if (reader == NULL) { | |
| return NULL; | |
| } | |
| reader->io.destroy = avifIOMemoryReaderDestroy; | |
| reader->io.read = avifIOMemoryReaderRead; | |
| reader->io.sizeHint = size; | |
| reader->io.persistent = AVIF_TRUE; | |
| reader->rodata.data = data; | |
| reader->rodata.size = size; | |
| return (avifIO *)reader; | |
| } | |
| // -------------------------------------------------------------------------------------- | |
| // avifIOFileReader | |
| typedef struct avifIOFileReader | |
| { | |
| avifIO io; // this must be the first member for easy casting to avifIO* | |
| avifRWData buffer; | |
| FILE * f; | |
| } avifIOFileReader; | |
| static avifResult avifIOFileReaderRead(struct avifIO * io, uint32_t readFlags, uint64_t offset, size_t size, avifROData * out) | |
| { | |
| // printf("avifIOFileReaderRead offset %" PRIu64 " size %zu\n", offset, size); | |
| if (readFlags != 0) { | |
| // Unsupported readFlags | |
| return AVIF_RESULT_IO_ERROR; | |
| } | |
| avifIOFileReader * reader = (avifIOFileReader *)io; | |
| // Sanitize/clamp incoming request | |
| if (offset > reader->io.sizeHint) { | |
| // The offset is past the EOF. | |
| return AVIF_RESULT_IO_ERROR; | |
| } | |
| uint64_t availableSize = reader->io.sizeHint - offset; | |
| if (size > availableSize) { | |
| size = (size_t)availableSize; | |
| } | |
| if (size > 0) { | |
| if (offset > AVIF_OFF_MAX) { | |
| return AVIF_RESULT_IO_ERROR; | |
| } | |
| if (reader->buffer.size < size) { | |
| AVIF_CHECKRES(avifRWDataRealloc(&reader->buffer, size)); | |
| } | |
| if (avif_fseeko(reader->f, (avif_off_t)offset, SEEK_SET) != 0) { | |
| return AVIF_RESULT_IO_ERROR; | |
| } | |
| size_t bytesRead = fread(reader->buffer.data, 1, size, reader->f); | |
| if (size != bytesRead) { | |
| if (ferror(reader->f)) { | |
| return AVIF_RESULT_IO_ERROR; | |
| } | |
| size = bytesRead; | |
| } | |
| } | |
| out->data = reader->buffer.data; | |
| out->size = size; | |
| return AVIF_RESULT_OK; | |
| } | |
| static void avifIOFileReaderDestroy(struct avifIO * io) | |
| { | |
| avifIOFileReader * reader = (avifIOFileReader *)io; | |
| fclose(reader->f); | |
| avifRWDataFree(&reader->buffer); | |
| avifFree(io); | |
| } | |
| avifIO * avifIOCreateFileReader(const char * filename) | |
| { | |
| FILE * f = fopen(filename, "rb"); | |
| if (!f) { | |
| return NULL; | |
| } | |
| if (avif_fseeko(f, 0, SEEK_END) != 0) { | |
| fclose(f); | |
| return NULL; | |
| } | |
| avif_off_t fileSize = avif_ftello(f); | |
| if (fileSize < 0) { | |
| fclose(f); | |
| return NULL; | |
| } | |
| if (avif_fseeko(f, 0, SEEK_SET) != 0) { | |
| fclose(f); | |
| return NULL; | |
| } | |
| avifIOFileReader * reader = (avifIOFileReader *)avifCalloc(1, sizeof(avifIOFileReader)); | |
| if (!reader) { | |
| fclose(f); | |
| return NULL; | |
| } | |
| reader->f = f; | |
| reader->io.destroy = avifIOFileReaderDestroy; | |
| reader->io.read = avifIOFileReaderRead; | |
| reader->io.sizeHint = (uint64_t)fileSize; | |
| reader->io.persistent = AVIF_FALSE; | |
| if (avifRWDataRealloc(&reader->buffer, 1024) != AVIF_RESULT_OK) { | |
| avifFree(reader); | |
| fclose(f); | |
| return NULL; | |
| } | |
| return (avifIO *)reader; | |
| } |