fasrt 0.3.0

A blazing fast, zero-copy subtitle parser and writer for SRT and WebVTT in Rust.
docs.rs failed to build fasrt-0.3.0
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.

A blazing fast, zero-copy subtitle parser and writer for SRT, WebVTT and ASS/SSA in Rust.

Installation

[dependencies]
fasrt = "0.3"

Features

  • Zero-copy, zero-allocation parsing — borrows directly from the input string
  • #![no_std] support (with optional alloc and std features)
  • Lazy iterator-based parsing — blocks are yielded on demand
  • DFA-based lexing via logos for fast tokenization
  • Strongly-typed newtypes (Hour, Minute, Second, Millisecond, Centisecond, Percentage) with compile-time validation
  • W3C WebVTT spec conformant — validated against Web Platform Tests

SRT

  • Strict and lossy parsing modes
  • Timestamps (HH:MM:SS,mmm)
  • Multiline cue bodies
  • Writer (std feature)

WebVTT

  • WEBVTT signature and header text
  • Timestamps (short MM:SS.mmm and long HH:MM:SS.mmm forms, unbounded hours)
  • Cue identifiers (zero-copy &str)
  • Cue settings (vertical, line, position, size, align, region)
  • NOTE, STYLE, REGION blocks
  • Full REGION definition parsing (id, width, lines, regionanchor, viewportanchor, scroll)
  • Float percentages (e.g., 50.5%)
  • CRLF, CR, LF line endings
  • BOM handling
  • Error recovery (--> in cue body, malformed timing lines)
  • Writer with round-trip fidelity (std feature)
  • Cue text parsing — two-layer design:
    • CueParser: logos DFA-backed, zero-alloc token stream (no_std)
    • CueText: W3C spec-compliant DOM tree builder with Node/TagNode types (alloc/std)
    • Tags: <b>, <i>, <u>, <c>, <ruby>, <rt>, <v>, <lang>, with classes and annotations
    • W3C tree building algorithm: implied end tags, <rt> scoping, unterminated tag handling
    • Full HTML5 named character reference support (2,231 entities via phf perfect hash map)
    • Numeric (&#32;) and hexadecimal (&#x20;) character references
    • Lazy text normalization via CueStr with OnceCell-cached decoding and NULL (U+0000 → U+FFFD) replacement

ASS/SSA

ASS/SSA has no formal specification; the behaviour below follows the SSA v4.00 specification document together with the VSFilter/libass renderers.

  • Sections ([Script Info], [V4 Styles], [V4+ Styles], [Events], [Fonts], [Graphics], and unknown sections verbatim)
  • Timestamps (H:MM:SS.cc, centiseconds, unpadded and unbounded hours)
  • Event rows (Dialogue:, Comment:, Picture:, Sound:, Movie:, Command:)
  • Format:-driven field order — EventFormat presets for ASS v4+, SSA v4 and Matroska packets
  • The Name (a.k.a. Actor) speaker column surfaced as first-class data
  • Style: rows exposed as zero-copy fields (style rendering semantics are out of scope)
  • [Fonts] / [Graphics] payload lines preserved verbatim, never mistaken for properties
  • Strict and lossy parsing modes
  • CRLF, CR, LF line endings, and BOM handling
  • Writer with round-trip fidelity (std feature)
  • Event text parsing — two-layer design, both usable standalone:
    • TextParser: logos DFA-backed, zero-alloc token stream (no_std, no alloc needed)
    • PlainText: clean-text extraction with OnceCell-cached lazy normalization, plus an allocation-free segments() iterator
    • Override blocks {…}, with libass tag boundaries: longest-match names (\fscx is not \fs + cx), spaces skipped after the backslash, and argument lists ending at the first ) (\t(0,500,\frz360) stays one tag)
    • libass brace semantics: { opens a block only when a } follows, \{ and \} are literal-brace escapes, so cleaning never deletes text a renderer would show
    • \N, \n and \h escapes; \p<n> drawing mode is tokenized and skipped, never interpreted as geometry
    • Columns a Format: line declares under an unrecognized name are preserved through a write, not dropped
  • Standalone event parsing for embedded Matroska S_TEXT/ASS tracks, which arrive one event per packet with container timing authoritative

Optional dependencies

Feature Default Description
std Yes Enables std::io writer and thiserror::Error impls
alloc No Enables CueText DOM tree and entity decoding without std
memchr Yes (via alloc/std) SIMD-accelerated fast path for entity decoding

Benchmarks

Measured on Apple Silicon with cargo bench (Criterion).

SRT

Benchmark Input Time Throughput
Parse (strict) 2 cues, 89 B ~170 ns 520 MiB/s
Parse (strict) 26 KB file ~38 µs 661 MiB/s
Parse (lossy) 332 files, ~8 MB ~12.1 ms 646 MiB/s
Collect into Vec 26 KB file ~40 µs 616 MiB/s

WebVTT

Benchmark Input Time Throughput
Parse 2 cues, 96 B ~318 ns 291 MiB/s
Parse Settings + region + style, 354 B ~915 ns 387 MiB/s
Parse All WPT fixtures, ~34 KB ~113 µs 314 MiB/s
Collect into Vec Settings + region + style, 354 B ~973 ns 364 MiB/s

Cue Text

Benchmark Input Time Throughput
Parse Tags only, 166 B ~316 ns 552 MiB/s
Parse 500 timestamps, ~11 KB ~14.1 µs 776 MiB/s

Run benchmarks yourself:

cargo bench

License

fasrt is under the terms of both the MIT license and the Apache License (Version 2.0).

See LICENSE-APACHE, LICENSE-MIT for details.

Copyright (c) 2026 FinDIT Studio authors.