Help us shape the future of Go! Take the 2026 Go Developer Survey

yenc

package
v0.3.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 3, 2026 License: GPL-3.0 Imports: 9 Imported by: 0

Documentation

Overview

Package yenc implements the yEnc binary encoding used for Usenet binary posts (http://www.yenc.org/yenc-draft.1.3.txt): a fast streaming decoder with =ybegin/=ypart/=yend parsing and CRC32 verification, and an encoder used by tests and the fake NNTP server.

yEnc maps every byte b to (b+42) mod 256. The "critical" output characters NUL, LF, CR and '=' are escaped as '=' followed by (b+42+64) mod 256; encoders also escape TAB and SPACE at line edges and '.' at the start of a line (to survive NNTP dot-stuffing done badly by some servers).

Index

Constants

View Source
const DefaultLineLength = 128

DefaultLineLength is the conventional encoded line length.

View Source
const DefaultMaxSize = 64 << 20

DefaultMaxSize bounds the buffer a header can make the decoder allocate. Usenet parts are typically 384 KiB-1 MiB; 64 MiB leaves ample headroom while stopping a hostile header from requesting gigabytes.

View Source
const MaxLineSize = 1 << 20

MaxLineSize limits each encoded line, including headers. Normal posts use 128 or 256 bytes; this allows unusually long lines without letting malformed input allocate an unbounded assembly or scratch buffer.

View Source
const MaxPreambleSize = 64 << 10

MaxPreambleSize bounds text skipped before the yEnc header.

Variables

View Source
var (
	// ErrNoHeader means no =ybegin line was found: the article is not yEnc.
	ErrNoHeader = errors.New("yenc: no =ybegin header")
	// ErrTruncated means the input ended before the =yend trailer.
	ErrTruncated = errors.New("yenc: data truncated (no =yend)")
	// ErrCRCMismatch means a pcrc32 or crc32 check failed.
	ErrCRCMismatch = errors.New("yenc: crc32 mismatch")
	// ErrSizeMismatch means the decoded length disagrees with the headers.
	ErrSizeMismatch = errors.New("yenc: size mismatch")
	// ErrMalformed covers unparseable keyword lines.
	ErrMalformed = errors.New("yenc: malformed header")
	// ErrTooLarge means the headers announce a part larger than the limit.
	ErrTooLarge = errors.New("yenc: part exceeds size limit")
)

Errors returned by the decoder. Decode errors are wrapped with context; use errors.Is to test for them.

Functions

func AppendEncode

func AppendEncode(dst, data []byte, o EncodeOptions) ([]byte, error)

AppendEncode appends the yEnc encoding of data (header, body lines and trailer, CRLF-terminated) to dst.

func DecodeLine

func DecodeLine(dst, src []byte, escaped bool) (int, bool)

DecodeLine decodes one encoded line (without line terminator) into dst and returns the number of bytes written. dst must have room for len(src) bytes. escaped carries a dangling '=' from the end of the previous line, and the returned flag reports a dangling '=' at the end of this one.

This is the hot loop. Each native word both detects '=' and subtracts 42 from eight independent byte lanes. Fusing detection with decoding avoids scanning each escaped run twice and calling a search routine per escape. The first set lane in the zero-byte mask is the first real escape even when subtraction borrows into a following lane. The short tail is scalar. No allocation, lookup table, unsafe access or architecture-specific ISA requirement is involved. Exact in-place decoding is supported.

func Encode

func Encode(w io.Writer, data []byte, o EncodeOptions) error

Encode writes the yEnc encoding of data to w.

func EncodeBytes

func EncodeBytes(data []byte, o EncodeOptions) ([]byte, error)

EncodeBytes returns the yEnc encoding of data.

func ParsePart

func ParsePart(line []byte, h *Header) error

ParsePart parses a "=ypart ..." line into h.

Types

type DecodeOptions

type DecodeOptions struct {
	// Buffer is reused for the decoded data when it has enough capacity.
	Buffer []byte
	// MaxSize caps the decoded part size; 0 means DefaultMaxSize.
	MaxSize int64
	// SkipCRC disables CRC32 verification (sizes are still checked).
	SkipCRC bool
}

DecodeOptions tunes decoding. The zero value is valid.

type EncodeOptions

type EncodeOptions struct {
	Name       string
	LineLength int // encoded characters per line; 0 = DefaultLineLength
	// Part > 0 produces a multipart post with a =ypart line. Total is the
	// number of parts, Begin the 1-based offset of data in the file and
	// FileSize the size of the whole file.
	Part     int
	Total    int
	Begin    int64
	FileSize int64
	// FileCRC, when HasFileCRC is set, is written as crc32= in the =yend of
	// a multipart post (the CRC of the whole file).
	FileCRC    uint32
	HasFileCRC bool
}

EncodeOptions describes the part being encoded.

type Header struct {
	Name  string // file name
	Line  int    // nominal encoded line length
	Size  int64  // size of the whole file
	Part  int    // 1-based part number, 0 for single-part posts
	Total int    // number of parts, 0 if not given
	// Begin and End are the 1-based inclusive byte offsets of this part in
	// the file. For single-part posts Begin is 1 and End is Size.
	Begin int64
	End   int64
	// Multipart is true when the post carried a =ypart line.
	Multipart bool
}

Header holds the =ybegin and (for multipart posts) =ypart fields.

func ParseBegin

func ParseBegin(line []byte) (Header, error)

ParseBegin parses a "=ybegin ..." line.

func (*Header) PartSize

func (h *Header) PartSize() int64

PartSize is the decoded size of this part announced by the header.

type LineReader

type LineReader interface {
	ReadLine() ([]byte, error)
}

LineReader yields one line at a time without its line terminator (LF or CRLF). The returned slice is only valid until the next call. At the end of input it returns io.EOF. The NNTP client implements it directly over its connection buffer so article bodies are decoded without an extra copy.

func NewLineReader

func NewLineReader(r io.Reader) LineReader

NewLineReader adapts an io.Reader to a LineReader. Lines longer than the internal buffer are assembled in a side buffer up to MaxLineSize bytes. An oversized line returns ErrTooLarge; the reader must then be discarded.

type Part

type Part struct {
	Header  Header
	Trailer Trailer
	Data    []byte
}

Part is one decoded article.

func Decode

func Decode(encoded []byte) (*Part, error)

Decode decodes a whole encoded article body held in memory.

func DecodeLines

func DecodeLines(lr LineReader, opt *DecodeOptions) (*Part, error)

DecodeLines decodes one yEnc part from lr. Lines before =ybegin are skipped. Decoding stops after the =yend line; any remaining lines are left unread so the caller can drain its transport.

When the data decodes but fails verification (ErrCRCMismatch or ErrSizeMismatch) the returned Part is non-nil and holds the data, so callers may choose to use it anyway.

func DecodeReader

func DecodeReader(r io.Reader, opt *DecodeOptions) (*Part, error)

DecodeReader decodes one part from r.

type Trailer

type Trailer struct {
	Size       int64 // decoded size of this part
	Part       int
	PartCRC32  uint32 // pcrc32: CRC of this part
	CRC32      uint32 // crc32: CRC of the whole file
	HasPartCRC bool
	HasCRC     bool
}

Trailer holds the =yend fields.

func ParseEnd

func ParseEnd(line []byte) (Trailer, error)

ParseEnd parses a "=yend ..." line.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL