blob: e43bdb5f2e708178cf989cded8ab4b695b8006e3 [file] [log] [blame]
// Copyright 2020 Joe Drago. All rights reserved.
// SPDX-License-Identifier: BSD-2-Clause
#include "avif/internal.h"
#include <limits.h>
#include <stdio.h>
#include <string.h>
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 *)avifAlloc(sizeof(avifIOMemoryReader));
if (reader == NULL) {
return NULL;
}
memset(reader, 0, sizeof(avifIOMemoryReader));
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 > LONG_MAX) {
return AVIF_RESULT_IO_ERROR;
}
if (reader->buffer.size < size) {
AVIF_CHECKRES(avifRWDataRealloc(&reader->buffer, size));
}
if (fseek(reader->f, (long)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;
}
fseek(f, 0, SEEK_END);
long fileSize = ftell(f);
if (fileSize < 0) {
fclose(f);
return NULL;
}
fseek(f, 0, SEEK_SET);
avifIOFileReader * reader = (avifIOFileReader *)avifAlloc(sizeof(avifIOFileReader));
if (!reader) {
fclose(f);
return NULL;
}
memset(reader, 0, sizeof(avifIOFileReader));
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;
}