Repository navigation
Conversation
RawTable copies every table out of the Resource into a new buffer, and a font.Font keeps the tables it parses. For a color emoji font this is most of the file: the 'sbix' table of macOS Apple Color Emoji.ttc is 182MB, so NewFont on it keeps 193MB on the Go heap. NewLoadersFromBytes builds the loaders from a []byte and returns each uncompressed table as a sub-slice of it, with its capacity limited to the table end. Compressed WOFF tables are still decoded into a new buffer. The input can be a read-only memory mapping, so the OS pages the tables in as they are used and can drop them again under memory pressure. The dst argument of RawTableTo is not used by such a loader: it may be a table returned before, which is then a view of the input. Measured on Apple Color Emoji.ttc, face 0, heap after GC with the Font alive: os.File + NewLoaders +193MB; mmap + NewLoadersFromBytes +11MB. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
TestParseCrashers already feeds random input to NewLoaders, which NewLoadersFromBytes calls. The loop also used math/rand.Read, which staticcheck flags (SA1019). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…table at EOF - Doc: the whole input stays reachable while any loader, font or data derived from them is in use; it must not be modified or unmapped; a returned table must never be passed as dst to a copying loader. - An empty table at offset == len(data) is now rejected, as ReadAt does on the copying path. - Tests: a compressed WOFF table with a view of the input as dst (fails without the dst guard); truncated input must accept and reject the same tables as NewLoaders; empty table at EOF. The old dst test is removed: it passed without the guard. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Hello, While I sympathize with the issue, I'm not sure I like the proposed solution that much. It seems the issue is that Could we instead introduce an interface extending Then, the new interface is easy to implement if you already have the whole resource as a byte slice. |
|
Thanks, that is cleaner. I'll rework it as an optional interface checked in
On the slice path, |
Yes, returning an error should be fine. I'm not sure about exporting a concrete type wrapping a slice. Maybe that would benefit other users who already have the full slice "in memory" ? |
|
Thanks. I'll add |
Loader.RawTablecopies every table out of theResourceinto a new buffer, and afont.Fontkeeps the tables it parses. For a color emoji font that is most of the file. Thesbixtable of macOSApple Color Emoji.ttcis 182 MB, soNewFonton it keeps 193 MB on the Go heap. A terminal that falls back to this font for one emoji grows by that much.This PR adds
NewLoadersFromBytes(data []byte). It is the same asNewLoaders, but it returns each uncompressed table as a sub-slice ofdataand does not copy it. The input can be a read-only memory mapping of the file. The OS then pages the tables in as they are used, and can drop the pages again under memory pressure.dstargument ofRawTableTois not used by such a loader.dstcan be a table that an earlier call returned (NewFontandfontscanpass the previous buffer back), and that table is now a view of the input.NewLoadersand its copying behavior do not change.I checked that nothing in
fontortableswrites into a table buffer. The only write found, inKernData1.parseValues, goes to a[4]bytearray field.Measured
Apple Color Emoji.ttc, face 0, heap after
runtime.GC()with theFontalive:os.File+NewLoaderssyscall.Mmap+NewLoadersFromBytesU+1F600still decodes as a PNGGlyphBitmap.Tests
commonandcollectionsfont (this includes a compressed WOFF) reads back the same as withNewLoaders.cap == len.RawTableTowith the previous table asdstleaves the input unchanged.font,sbixglyph data from the Sbix toy fonts is the same as withNewLoaders, and it points into the input.go test ./...passes on Go 1.19.13 and on stable.go vetandstaticcheckv0.8.0 are clean.🤖 Generated with Claude Code