blob: 3dbf278b37f588fe75e60beaf634b0171254063d [file]
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2025, Syoyo Fujita and many contributors.
// All rights reserved.
//
// StreamReader: Simple header-only stream reader with endian support
//
// Part of TinyEXR V2 API (EXPERIMENTAL)
#ifndef TINYEXR_STREAMREADER_HH_
#define TINYEXR_STREAMREADER_HH_
#include <cstdint>
#include <cstring>
namespace tinyexr {
enum class Endian {
Little,
Big,
Native
};
class StreamReader {
public:
// Constructor: takes memory address, length, and endianness
StreamReader(const uint8_t* data, size_t length, Endian endian = Endian::Little)
: data_(data), length_(length), pos_(0), endian_(endian) {
// Detect native endian
uint16_t test = 0x0001;
native_is_little_ = (*reinterpret_cast<uint8_t*>(&test) == 0x01);
// Determine if we need to swap bytes
if (endian_ == Endian::Native) {
needs_swap_ = false;
} else {
bool data_is_little = (endian_ == Endian::Little);
needs_swap_ = (data_is_little != native_is_little_);
}
}
// Read n bytes into destination buffer
// Returns false on out-of-bounds or error
bool read(size_t n, uint8_t* dst) {
if (!dst || n == 0) {
return false;
}
if (pos_ + n > length_) {
return false; // Out of bounds
}
std::memcpy(dst, data_ + pos_, n);
pos_ += n;
return true;
}
// Read 1 byte (uint8_t)
bool read1(uint8_t* dst) {
return read(1, dst);
}
// Read 2 bytes (uint16_t) with endian swap if needed
bool read2(uint16_t* dst) {
if (!dst) {
return false;
}
uint8_t buf[2];
if (!read(2, buf)) {
return false;
}
if (needs_swap_) {
*dst = static_cast<uint16_t>(buf[1]) << 8 |
static_cast<uint16_t>(buf[0]);
} else {
std::memcpy(dst, buf, 2);
}
return true;
}
// Read 4 bytes (uint32_t) with endian swap if needed
bool read4(uint32_t* dst) {
if (!dst) {
return false;
}
uint8_t buf[4];
if (!read(4, buf)) {
return false;
}
if (needs_swap_) {
*dst = static_cast<uint32_t>(buf[3]) << 24 |
static_cast<uint32_t>(buf[2]) << 16 |
static_cast<uint32_t>(buf[1]) << 8 |
static_cast<uint32_t>(buf[0]);
} else {
std::memcpy(dst, buf, 4);
}
return true;
}
// Read 8 bytes (uint64_t) with endian swap if needed
bool read8(uint64_t* dst) {
if (!dst) {
return false;
}
uint8_t buf[8];
if (!read(8, buf)) {
return false;
}
if (needs_swap_) {
*dst = static_cast<uint64_t>(buf[7]) << 56 |
static_cast<uint64_t>(buf[6]) << 48 |
static_cast<uint64_t>(buf[5]) << 40 |
static_cast<uint64_t>(buf[4]) << 32 |
static_cast<uint64_t>(buf[3]) << 24 |
static_cast<uint64_t>(buf[2]) << 16 |
static_cast<uint64_t>(buf[1]) << 8 |
static_cast<uint64_t>(buf[0]);
} else {
std::memcpy(dst, buf, 8);
}
return true;
}
// Seek to absolute position
// Returns false if position is out of bounds
bool seek(size_t pos) {
if (pos > length_) {
return false;
}
pos_ = pos;
return true;
}
// Rewind to beginning
void rewind() {
pos_ = 0;
}
// Get current position
size_t tell() const {
return pos_;
}
// Get remaining bytes
size_t remaining() const {
return length_ - pos_;
}
// Check if at end
bool eof() const {
return pos_ >= length_;
}
// Get total length
size_t length() const {
return length_;
}
private:
const uint8_t* data_;
size_t length_;
size_t pos_;
Endian endian_;
bool native_is_little_;
bool needs_swap_;
};
} // namespace tinyexr
#endif // TINYEXR_STREAMREADER_HH_