# ByteReader _(no submodules)_ ## Purpose Provides a low-level, bounds-checked binary reader for TIFF/EXIF byte streams. It wraps a `Uint8Array` with a configurable byte order and exposes typed read methods for all TIFF primitive types. The runtime-agnostic implementation avoids Node.js `Buffer` and browser-specific APIs, relying solely on `DataView` and standard typed arrays. ## Features - Big-endian and little-endian support with runtime `setByteOrder()` switching - Full TIFF primitive coverage: uint8/16/32, int16/32, float32/64, rational, signed rational - Bounds-checked reads that throw `ExifError` with `EXIF.PARSE_ERROR` on overflow - String reads with null-termination handling (`readString`, `readNullTermString`) - `slice()` factory that creates a sub-reader sharing the same byte order ## Usage: Basic Example ```ts import { ByteReader } from "@suaveplan/exif/binary"; const data = new Uint8Array([0x4D, 0x4D, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x08]); const reader = new ByteReader(data, "BE"); const magic = reader.readUint16(2); // 0x002A const ifdOffset = reader.readUint32(4); // 8 ``` ## Usage: Advanced Example ```ts import { ByteReader } from "@suaveplan/exif/binary"; // Switch byte order mid-stream (e.g. after reading TIFF header) const reader = new ByteReader(tiffBuffer, "BE"); const bom = reader.readUint16(0); if (bom === 0x4949) reader.setByteOrder("LE"); // Intel = little-endian // Read a RATIONAL (8 bytes: numerator + denominator) const exposureTime = reader.readRational(offset); console.log(`${exposureTime.numerator}/${exposureTime.denominator}`); // Sub-slice for IFD parsing const ifdReader = reader.slice(ifdOffset, ifdOffset + ifdSize); const entryCount = ifdReader.readUint16(0); ``` ## API Reference ### `constructor(data: Uint8Array, byteOrder?: ByteOrder): ByteReader` Creates a new reader wrapping `data`. Default `byteOrder` is `"BE"` (big-endian). ### `setByteOrder(order: ByteOrder): void` Switches the active byte order for all subsequent reads. ### `get length: number` Returns the total byte length of the underlying buffer. ### `readUint8(offset: number): number` Reads one unsigned byte at `offset`. ### `readUint16(offset: number): number` Reads a 2-byte unsigned integer using the active byte order. ### `readUint32(offset: number): number` Reads a 4-byte unsigned integer using the active byte order. ### `readInt16(offset: number): number` Reads a 2-byte signed integer using the active byte order. ### `readInt32(offset: number): number` Reads a 4-byte signed integer using the active byte order. ### `readFloat32(offset: number): number` Reads a 4-byte IEEE 754 single-precision float using the active byte order. ### `readFloat64(offset: number): number` Reads an 8-byte IEEE 754 double-precision float using the active byte order. ### `readBytes(offset: number, length: number): Uint8Array` Returns a copy of `length` bytes starting at `offset`. ### `readString(offset: number, length: number): string` Reads `length` bytes as a Latin-1 string, stopping at the first null byte. ### `readNullTermString(offset: number): string` Scans forward from `offset` until a null byte, then reads the preceding bytes as a string. ### `readRational(offset: number): Rational` Reads an 8-byte TIFF RATIONAL (two unsigned 32-bit integers: numerator then denominator). ### `readSRational(offset: number): Rational` Reads an 8-byte TIFF SRATIONAL (two signed 32-bit integers). ### `slice(start: number, end: number): ByteReader` Returns a new `ByteReader` over `data.slice(start, end)` with the same byte order. ## Implementation Notes All read methods call `assertBounds` before touching `DataView` to ensure clear error messages on malformed input. The `view` is constructed from the array's `buffer`, `byteOffset`, and `byteLength` to correctly handle sliced `Uint8Array` inputs that do not start at offset 0 in their underlying `ArrayBuffer`. The `readString` and `readNullTermString` methods use Latin-1 (one byte per character) which matches TIFF/EXIF ASCII strings; UTF-8 decoding is not attempted here as EXIF ASCII tags are defined as 7-bit ASCII.