refactor: value objects for all of ZstdFrameHeader's size fields#103
Merged
Conversation
Removes the last naked numeric fields from ZstdFrameHeader: - blockSizeMax(): masked long -> ZstdByteSize (always non-negative) - headerSize(): int -> ZstdByteSize (now matches ZstdFrame.headerSize()) - the raw `long frameContentSize` component is gone; contentSize() is now a real Optional<ZstdByteSize> record component - windowSize(): long -> Optional<ZstdByteSize> contentSize and windowSize are Optional because a hostile header can declare either as an unsigned value at or above 2^63 (zstd copies the content-size field verbatim, and a single-segment frame sets windowSize equal to it), which is not representable as a non-negative size. Mapping those to empty keeps ZstdFrame.header() parsing total instead of throwing on hostile input — the sign-bit test now asserts both contentSize() and windowSize() go empty. The shared ZstdByteSize.fromFrameHeaderContentSize helper is generalized to fromUnsignedFrameHeaderField since it now wraps both fields. Also resolves the headerSize() asymmetry from the previous PR: ZstdFrame.headerSize() and ZstdFrameHeader.headerSize() now both return ZstdByteSize. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
dfa1
force-pushed
the
refactor/frame-header-value-objects
branch
from
July 25, 2026 16:56
ea1e0ce to
c10ea68
Compare
Records why only the flag — not the checksum value — appears here: the content-checksum bit is header data (Frame_Header_Descriptor), but the 4-byte XXH64 checksum is a frame trailer over the decompressed content, so it is not part of the parsed header. Notes the flag's practical use: gating on integrity before decompressing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.
Why
Final piece of the API-stabilization audit, per follow-up feedback: no naked numeric fields left on
ZstdFrameHeader— the raw parsed-struct record.What changed
blockSizeMax()longZstdByteSizeheaderSize()intZstdByteSizecontentSize()long frameContentSizeOptional<ZstdByteSize>component (rawframeContentSizeremoved)windowSize()longOptional<ZstdByteSize>Why
Optional, not plainZstdByteSize, for two of themA hostile header can declare the content size — and, for a single-segment frame, the window size zstd mirrors from it — as an unsigned value at or above
2^63. That wraps negative in a signedlongand is not representable as a non-negativeZstdByteSize. Wrapping it directly would turnZstdFrame.header()parsing into a thrown exception on hostile input (a DoS-shaped regression).Optional<ZstdByteSize>is the honest value-object representation: present when representable, empty otherwise. The existing sign-bit test now asserts bothcontentSize()andwindowSize()go empty.The shared
ZstdByteSize.fromFrameHeaderContentSizehelper is generalized tofromUnsignedFrameHeaderFieldsince it now wraps both fields.Bonus
Resolves the
headerSize()asymmetry noted in #102:ZstdFrame.headerSize()andZstdFrameHeader.headerSize()now both returnZstdByteSize.Note
contentSize/windowSizeare nowOptionalrecord components. If your SonarQube profile flagsOptionalas a component (S3553-adjacent), say so and I'll adjust — but modern Sonar exempts records, and this is the cleanest "always a value object" shape.Testing
./mvnw clean testgreen (324); validate + javadoc + checkstyle clean; reactor compiles.🤖 Generated with Claude Code