Repository navigation
Own the encoder and the digest, and cut what a caller links by two thirds - #13
Merged
Merged
Conversation
A program whose whole job is to turn a version string into one small picture
was linking a general-purpose compressor, a general-purpose compositor and the
FIPS-140 module to do it. None of the three earns its place:
- internal/pngenc replaces image/png with a fixed-Huffman deflate stream and
the four chunks PNG actually needs. The committed gallery grows by 0.4%.
- internal/sha256 replaces crypto/sha256, which now arrives behind the
FIPS-140 module's self-tests and entropy sources. The digests are the same
digests, so every mark is unchanged, and a test checks that against the
standard library.
- canvas.fillRect and blazontest.blit replace the two image/draw calls that
were the package's only use of it.
Measured on a program that renders one 256px PNG, built with -s -w:
before 2,444,288
after 1,951,232 (-493,056, -20%; an empty Go binary is 1,497,088)
The gallery is regenerated and is byte-identical to what the code renders.
TestNoHeavyStandardLibraryImports keeps the imports from drifting back, since
a single fmt.Errorf on an image type would bring one of them with it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019RJ8FUiT1YvbBz3D2PriM9
The size argument for owning the encoder and the digest was made with numbers from one laptop, which is where such numbers usually stay. This adds the leg that keeps them true. TinyGo is the scale, because it has no reflection-heavy runtime to hide behind: one fmt.Errorf on an image type is hundreds of kilobytes there rather than noise. internal/sizeprobe is the thing being weighed — the smallest program that renders a mark, so that what the scale reads is this library and not a CLI's flag parsing. The leg also renders 1.4.2 under both compilers and compares the files byte for byte. gc and LLVM are not obliged to agree about floats and the renderers are full of them, so this is a real check rather than a formality; today they agree exactly, including for the float-heavy default renderer. The root package's suite is left out of the TinyGo run: it renders whole corpora and exhausts the wasm heap. TestRegisterRejectsDuplicates moves behind a !tinygo tag for a related reason — TinyGo's recover does not catch a panic from another function, and its t.Skip does not unwind, so the test would take the process down instead of reporting. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019RJ8FUiT1YvbBz3D2PriM9
fmt was the last heavy import, and by some distance the worst value for money:
it exists here to join strings that are already strings. Every coordinate in
the SVG back end arrives from num() pre-formatted, every colour from hex(),
every escape from three integers. The format strings were decorative, and the
reflection behind them was not.
What changed, in order of how much it saved:
- canvas/svg.go and canvas/ansi.go assemble their markup and their escapes
directly. The SVG and ANSI bytes are unchanged, which the golden files and
the untouched gallery both say.
- version.go grows a syntaxError that wraps ErrSyntax, so errors.Is keeps
working; the messages are byte-identical to what fmt.Errorf produced.
- blazon.go spells out the unknown-renderer error, and takes slices.Sort
over sort.Strings, since package sort imports reflect.
- internal/sha256 and the entropy streams shift their own big-endian
counters rather than importing encoding/binary, which also imports
reflect.
fmt, reflect, sort and encoding/binary are now absent from what a caller
links, and TestNoHeavyStandardLibraryImports says so. blazontest and
cmd/blazon keep fmt: their callers already link testing or flag, which bring
reflection regardless.
Measured on internal/sizeprobe:
gc, -s -w 1,951,232 -> 1,783,808
tinygo/wasi 1,011,974 -> 785,684 (-22%)
Cumulatively, across this and the two commits before it, the library's cost
above an empty Go binary falls from 947 KB to 287 KB. The CI budget drops to
950,000 bytes to match, tight enough that fmt cannot come back unnoticed.
Both compilers still render 1.4.2 to the same bytes, and those bytes are the
same ones as before this change.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019RJ8FUiT1YvbBz3D2PriM9
The library's cost to a caller has changed enough to be worth a version: the PNG encoder, the digest, the rasteriser's fills and the string formatting are all this repository's own now, and what blazon adds to a binary above an empty Go program fell from 947 KB to 287 KB. No rendered mark moved. The logo is regenerated because it is the flowfield mark for this exact version, which is the whole point of signing releases with our own output. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019RJ8FUiT1YvbBz3D2PriM9
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
blazon told callers it had no dependencies, which was true of
go.modand notof what it put in their binaries. A program whose whole job is to turn a
version string into one small picture was linking a general-purpose
compressor, a general-purpose compositor, the FIPS-140 module and the
reflection runtime to do it.
All four are gone. Nothing a version renders to has changed — the committed
gallery is byte-identical, and the marks now come out the same under gc and
under LLVM.
What replaced them
image/png,compress/flate,hash/crc32internal/pngenccrypto/sha256internal/sha256image/drawcanvas.fillRect,blazontest.blitfmt, and with itreflectsort,encoding/binaryslices.Sort, byte shiftsreflect, which is what actually cost the kilobytes.version.gokeepserrors.Is(err, ErrSyntax)working through asyntaxErrorthat wraps it, and its messages are byte-identical to what
fmt.Errorfproduced.
What it costs a caller
Measured on
internal/sizeprobe, the smallest program that renders a mark:-s -wKeeping it that way
Two new rules, because these imports come back by accident and nothing else
would notice — one
fmt.Errorfon an image type is enough.TestNoHeavyStandardLibraryImportsfails the build if any of them reappearsin what a caller links.
blazontestandcmd/blazonare exempt: theircallers already link
testingorflag.tinygoCI leg weighsinternal/sizeprobeagainst a byte budget, andrenders
1.4.2under both compilers to compare the files byte for byte. gcand LLVM are not obliged to agree about floats and the renderers are full of
them; today they agree exactly, including for the float-heavy default
renderer.
The root package's suite is left out of the TinyGo run — it renders whole
corpora and exhausts the wasm heap.
TestRegisterRejectsDuplicatesmovesbehind a
!tinygotag: TinyGo'srecoverdoes not catch a panic from anotherfunction and its
t.Skipdoes not unwind, so the test would take the processdown instead of reporting.
Released as 0.2.0, with the logo regenerated for that version.
🤖 Generated with Claude Code
https://claude.ai/code/session_019RJ8FUiT1YvbBz3D2PriM9