POSIX ustar tar archive header encoding and decoding.

A tar archive is a sequence of fixed-size 512-byte blocks. Each member (file, directory, link, ...) starts with one 512-byte ustar header block, optionally followed by the member's data rounded up to a multiple of 512 bytes:

offset  size  field
------  ----  -----------------------------------------
     0   100  name
   100     8  mode      (octal ASCII)
   108     8  uid       (octal ASCII)
   116     8  gid       (octal ASCII)
   124    12  size      (octal ASCII)
   136    12  mtime     (octal ASCII)
   148     8  checksum  (octal ASCII)
   156     1  typeflag
   157   100  linkname
   257     6  magic     ("ustar" + NUL)
   263     2  version
   265    32  uname
   297    32  gname
   329     8  devmajor
   337     8  devminor
   345   155  prefix
   500    12  padding
------  ----  -----------------------------------------
           512  total

All multi-character fields are ASCII, left-justified and NUL-padded to their field width. mode, uid, gid, size, mtime and checksum are additionally numeric: an octal number rendered as ASCII digits, zero-padded and NUL-terminated within the field. This coder refines those six fields to and from number, and trims the NUL padding from every other string field transparently.

devmajor and devminor only carry meaning for the character-special and block-special typeflags; they are decoded as trimmed strings rather than numbers since ustar leaves their content undefined for every other typeflag, including the TAR_TYPEFLAG values this coder targets.

This package covers a single 512-byte header block only. It does not walk a multi-member archive, does not size or read the data blocks that follow a header, and does not compute or verify the checksum field — all of that is left to the caller for now.

Examples

Round-trip a regular-file header

import { assertEquals } from "@std/assert";
import {
  TAR_BLOCK_SIZE,
  TAR_TYPEFLAG,
  USTAR_MAGIC,
  USTAR_VERSION,
  ustarHeader,
} from "@binstruct/tar";

const coder = ustarHeader();
const header = {
  name: "hello.txt",
  mode: 0o644,
  uid: 1000,
  gid: 1000,
  size: 5,
  mtime: 1_700_000_000,
  checksum: 0,
  typeflag: TAR_TYPEFLAG.regularFile,
  linkname: "",
  magic: USTAR_MAGIC,
  version: USTAR_VERSION,
  uname: "user",
  gname: "user",
  devmajor: "",
  devminor: "",
  prefix: "",
  padding: new Uint8Array(12),
};

const buffer = new Uint8Array(TAR_BLOCK_SIZE);
const written = coder.encode(header, buffer);
const [decoded, read] = coder.decode(buffer);

assertEquals(written, TAR_BLOCK_SIZE);
assertEquals(read, TAR_BLOCK_SIZE);
assertEquals(decoded, header);