// 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 #include 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(&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(buf[1]) << 8 | static_cast(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(buf[3]) << 24 | static_cast(buf[2]) << 16 | static_cast(buf[1]) << 8 | static_cast(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(buf[7]) << 56 | static_cast(buf[6]) << 48 | static_cast(buf[5]) << 40 | static_cast(buf[4]) << 32 | static_cast(buf[3]) << 24 | static_cast(buf[2]) << 16 | static_cast(buf[1]) << 8 | static_cast(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_