@libs-jd/diff-fast
v0.1.0
Published
A drop-in replacement for `diff` (jsdiff) with a V8-optimized Myers implementation for diffChars and diffLines — element-for-element identical output, ~5x faster on char diffs and ~3-5x on line diffs. Everything else in the diff API is re-exported unchang
Maintainers
Readme
diff-fast
A drop-in replacement for diff (jsdiff) with a V8-optimized Myers implementation for the two hottest entry points — diffChars and diffLines. Same API, element-for-element identical output, meaningfully faster on non-trivial inputs.
// change this
import { diffChars, diffLines, diffWords } from "diff";
// to this
import { diffChars, diffLines, diffWords } from "@libs-jd/diff-fast";diffChars and diffLines are the optimized ones; everything else (diffWords, diffWordsWithSpace, diffSentences, diffCss, diffJson, diffArrays, createPatch, structuredPatch, applyPatch, parsePatch, convertChangesToXML, …) is re-exported from diff unchanged.
Why
jsdiff's Myers core allocates a linked chain of component objects, keeps a sparse hash of diagonals, and calls Date.now() on every iteration for its optional timeout. This rewrite keeps the algorithm and the tie-breaking bit-for-bit identical but:
- uses a dense typed-array for the diagonal frontier instead of a sparse object map,
- stores path components in a flat parallel typed-array pool (no per-node object churn),
- drops
Date.now()from the default hot path, - inlines the
===token-equality fast path.
Only the default-options path is optimized. Any non-default option (ignoreCase, oneChangePerToken, maxEditLength, timeout, callback, a custom comparator, ignoreWhitespace, stripTrailingCr, …) is transparently delegated to stock jsdiff, so behavior always matches.
Benchmark
Measured against [email protected], best-of-7, Apple M-series, Bun 1.3 / V8. Reproduce with bun run bench/bench.ts.
| Workload | stock diff | @libs-jd/diff-fast | Speedup |
| --- | ---: | ---: | ---: |
| diffChars — small, identical (n=300) | 0.038 ms | 0.038 ms | 1.0x (neutral) |
| diffChars — medium, ~12% edits (n=2500) | 60.4 ms | 7.7 ms | 7.8x |
| diffChars — large, ~40% edits (n=3000) | 1226 ms | 211 ms | 5.8x |
| diffLines — small, identical (60 lines) | 0.055 ms | 0.065 ms | 0.85x (neutral) |
| diffLines — medium, ~12% edits (800 lines) | 6.1 ms | 4.2 ms | 1.4x |
| diffLines — large, ~40% edits (2000 lines) | 544 ms | 82 ms | 6.7x |
Honest take: on tiny or identical inputs the two are a wash (~1x, and occasionally a hair slower) — there's little work to save and jsdiff's overhead there is already trivial. The win grows as inputs get larger and more divergent — roughly 5–8x on character diffs and up to ~7x on large line diffs — exactly where diffing actually costs you real milliseconds. Numbers vary by machine and runtime; run the bench on yours.
Identical output — verified
The optimized path is asserted element-for-element identical to jsdiff 9.0.0 across a diverse corpus (identical, empty, pure-insert, pure-delete, random char/line edits at several rates, transpositions and other tie-break-sensitive shapes, surrogate pairs, CRLF, trailing-newline): 200 cases, 0 mismatches. Change objects even preserve jsdiff's exact key order {count, added, removed, value}. The corpus runs on every bun test.
Install
npm install @libs-jd/diff-fast
# diff is a dependency and comes along automaticallyUsage
Identical to jsdiff — see the jsdiff docs.
import { diffChars, diffLines } from "@libs-jd/diff-fast";
for (const part of diffChars("beep boop", "beep boob blah")) {
// part: { count, added, removed, value }
if (part.added) process.stdout.write(`+${part.value}`);
else if (part.removed) process.stdout.write(`-${part.value}`);
else process.stdout.write(part.value);
}// re-exported unchanged from `diff`
import { diffWords, createPatch, applyPatch } from "@libs-jd/diff-fast";
const patch = createPatch("file.txt", "a\nb\nc\n", "a\nB\nc\n");Compatibility
- Output, key order, and tie-breaking match
[email protected]. - ESM + CJS builds, full TypeScript types (mirrors jsdiff's
Changeshape). - All non-
diffChars/diffLinesexports are the genuinediffimplementations.
About
Contributions welcome — please open an issue or PR. If this shaved time off your diffs, a ⭐ helps others find it.
Related
- diff / jsdiff — the library this builds on and stays compatible with
- @libs-jd/pdf-lib-bulk — bulk PDF generation for pdf-lib
Author
Jeet Dhandha — GitHub
