mirror of
https://github.com/bkaradzic/bimg.git
synced 2026-06-08 10:53:46 +00:00
172 lines
3.7 KiB
C++
172 lines
3.7 KiB
C++
// 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_
|