first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,117 @@
# ByteReader
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## 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.