Revision control

++ // 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_