TCP segment encoding and decoding utilities (RFC 9293).

A TCP segment is a 20-byte fixed header, an optional 0–40 byte options trailer (sized by the dataOffset field), and a variable-length payload:

 0      7 8     15 16    23 24    31
+--------+--------+--------+--------+
|     Source      |   Destination   |
|      Port       |      Port       |
+--------+--------+--------+--------+
|          Sequence Number          |
+--------+--------+--------+--------+
|       Acknowledgment Number       |
+--------+--------+--------+--------+
| DOff |Rsrvd|C E U A P R S F| Window|
| 4b   | 4b  |W C R C S S Y I| 16 bit|
|      |     |R E G K H T N N|       |
+--------+--------+--------+--------+
|    Checksum     | Urgent Pointer  |
+--------+--------+--------+--------+
|        Options (0-40 bytes)       |
+-----------------------------------+
|        Payload (variable)         |
+-----------------------------------+

Unlike UDP, TCP carries no segment-length field — the payload size must come from the carrier (typically IPv4.totalLength - IPv4.ihl * 4). The coder therefore absorbs the rest of the buffer it is given as the payload, so it is round-trip-safe only when handed a buffer slice already trimmed to the segment length. @binstruct/inet's inetFrame() does this trimming; stand- alone callers should slice before decoding.

The checksum field is computed over a TCP pseudo-header followed by the segment; assembling the pseudo-header is the caller's job. Use internetChecksum from @binstruct/inet once the bytes are concatenated.

Examples

Round-trip a SYN segment with no options or payload

import { assertEquals } from "@std/assert";
import { TCP_HEADER_MIN_SIZE, tcpPacket } from "@binstruct/tcp";

const coder = tcpPacket();
const segment = {
  sourcePort: 49152,
  destinationPort: 80,
  sequenceNumber: 0xdeadbeef,
  acknowledgmentNumber: 0,
  dataOffsetReserved: { dataOffset: 5, reserved: 0 },
  flags: { cwr: 0, ece: 0, urg: 0, ack: 0, psh: 0, rst: 0, syn: 1, fin: 0 },
  window: 65535,
  checksum: 0,
  urgentPointer: 0,
  options: new Uint8Array(0),
  payload: new Uint8Array(0),
};

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

assertEquals(written, TCP_HEADER_MIN_SIZE);
assertEquals(read, TCP_HEADER_MIN_SIZE);
assertEquals(decoded.flags.syn, 1);
assertEquals(decoded.sequenceNumber, 0xdeadbeef);