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,100 @@
# ByteWriter
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Provides a dynamic binary writer for building TIFF/EXIF byte streams from scratch. It maintains an auto-growing `Uint8Array` buffer and exposes typed write methods for all TIFF primitive types. This is the write-side counterpart to `ByteReader`, sharing the same byte-order model and error infrastructure.
## Features
- Auto-growing buffer (doubles capacity on overflow, no pre-allocation required)
- Big-endian and little-endian support with runtime `setByteOrder()` switching
- Full TIFF primitive coverage: uint8/16/32, int16, rational, raw bytes, strings
- Fluent API — all write methods return `this` for chaining
- `toUint8Array()` snapshot of the written bytes at any point
- `ExifError` with `EXIF.WRITE_ERROR` on non-Latin-1 string characters
## Usage: Basic Example
```ts
import { ByteWriter } from "@suaveplan/exif/binary";
const writer = new ByteWriter(64, "BE");
writer.writeUint16(0x4D4D); // big-endian BOM
writer.writeUint16(0x002A); // TIFF magic
writer.writeUint32(8); // IFD0 offset
const bytes = writer.toUint8Array();
// Uint8Array [ 0x4D, 0x4D, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x08 ]
```
## Usage: Advanced Example
```ts
import { ByteWriter } from "@suaveplan/exif/binary";
// Fluent chaining
const exifHeader = new ByteWriter()
.setByteOrder("LE")
.writeBytes(new Uint8Array([0x45, 0x78, 0x69, 0x66, 0x00, 0x00])) // "Exif\0\0"
.writeUint16(0x4949) // LE BOM
.writeUint16(0x002A)
.writeUint32(8)
.toUint8Array();
// Writing a RATIONAL (e.g. exposure time 1/100)
const writer = new ByteWriter();
writer.writeRational({ numerator: 1, denominator: 100 });
```
## API Reference
### `constructor(initialSize?: number, byteOrder?: ByteOrder): ByteWriter`
Creates a new writer. `initialSize` defaults to 256 bytes. `byteOrder` defaults to `"BE"`.
### `get position: number`
Returns the current write cursor position (total bytes written so far).
### `setByteOrder(order: ByteOrder): this`
Switches the active byte order. Returns `this` for chaining.
### `writeUint8(value: number): this`
Writes one unsigned byte.
### `writeUint16(value: number): this`
Writes a 2-byte unsigned integer using the active byte order.
### `writeUint32(value: number): this`
Writes a 4-byte unsigned integer using the active byte order.
### `writeInt16(value: number): this`
Writes a 2-byte signed integer using the active byte order.
### `writeBytes(bytes: Uint8Array): this`
Appends a raw byte array.
### `writeString(str: string): this`
Writes a Latin-1 string without null termination. Throws `ExifError(WRITE_ERROR)` if any character is outside the Latin-1 range (> U+00FF).
### `writeRational(r: Rational): this`
Writes an 8-byte TIFF RATIONAL (two unsigned 32-bit integers: numerator then denominator).
### `toUint8Array(): Uint8Array`
Returns a copy of all bytes written so far (from position 0 to the current cursor).
## Implementation Notes
The buffer starts at `initialSize` and doubles whenever the next write would overflow. This amortizes allocation cost to O(log n) reallocations for n total bytes written. The `view` is recreated after each reallocation since `DataView` binds to a specific `ArrayBuffer` instance. `toUint8Array()` always returns a fresh slice to prevent the caller from holding a reference to the internal buffer.