Skip to content

perf(parser): parse segment fields on first read and keep received bytes - #42

Merged
shadowhand merged 6 commits into
mainfrom
feature/sv-77-defer-segment-field-parsing-until-first-access-lazy-fields
Oct 17, 2026
Merged

shadowhand merged 6 commits into
mainfrom
feature/sv-77-defer-segment-field-parsing-until-first-access-lazy-fields

Conversation

@shadowhand

Copy link
Copy Markdown
Contributor

Goal

Parse a segment's fields only when they are read, so unread fields cost nothing, a malformed field throws only when that field is read, and parse() followed by serialize() is byte-exact except for fields that actually changed, as SV-77 specifies.

Changes

  • AbstractSegment::parse() keeps the line and its Encoding. The line is split into wire field strings on first need, without building fields, and each field is parsed the first time it is read. A failed parse leaves no partial state, and the next read throws again.
  • getFieldCount(), getNames(), isRequired() and maxRepetitions() split the line without parsing fields, so generic segments and extra vendor fields report the same values as before.
  • serialize() emits the received line for an unread segment when the delimiters match (Encoding::sameDelimiters(), which ignores the line ending). Otherwise each field keeps its received bytes unless its content now encodes differently. The received field count is kept (joinTrimmed() gains a $keep minimum). Different delimiters re-encode every field, as before.
  • MSH overrides only the field mapping (MSH-1 is the separator, MSH-2 is verbatim) and the header. Reading a field never rewrites a received MSH-2.
  • No changes to the Type layer. This replaces the dirty-flag design from perf(parser): defer segment field parsing until first access #40, which is closed.
  • SV-83's PID test now expects the maxReps violation when PID.1 is read, not from parse().
  • README (lazy parsing, byte-exact round trip, which Encoding to serialize with, read-time errors) and CHANGELOG updated.

On a 111-segment ADT_A01 with 100 NK1 (PHP 8.5, opcache): parse 5.3 → 0.7 ms; parse + serialize, unread, 28.2 → 0.7 ms; one PID-5 write changes 1 line instead of 105.

Refs SV-77

A segment now keeps its line on parse() and parses each field the first time it is read. Asking for the field table splits the line without building fields. A malformed field throws only when that field is read, and a failed read leaves no partial state.

Refs SV-77
With the delimiters a segment was parsed with, an unread segment serializes as its received line. A read segment keeps each field's received bytes unless the field's content now encodes differently from what was received. Writing a field re-encodes only that field. Different delimiters still re-encode every field.

Refs SV-77
Reading a field no longer rewrites a segment's leading parts from the serializing encoding, so a received MSH.2 that declares characters the encoding does not (such as a truncation character) survives a read. Round-trip tests now cover CRLF input and reading every field. The README says which Encoding the byte-exact guarantee needs.

Refs SV-77
With fields parsed on first read, SV-83's repetition check fires on reading PID.1 rather than from parse().

Refs SV-77
@github-actions

github-actions Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

The Robin Hood of code review

✅ Review done. I flagged 1 thing worth a look.

Push fixes whenever you like, then comment /robin for another pass.

Robin v2.12.0 · antongulin/robin@main (fc0bf91) · agent review: 3 turns · 10 tool calls

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Robin Hood of code review

Heads up: this is a point-in-time review. Push fixes freely, then comment /robin whenever you want another pass.

⚠️ 1 Medium

Summary

The change adds lazy field materialization and preserves received wire bytes while providing focused tests for deferred validation and round-tripping. One reparse edge case remains: dynamically added field definitions survive parsing new, shorter content, so the segment can report fields that are no longer present.

Comment thread src/AbstractSegment.php
Generic definitions added for a line's extra wire fields, or for reading past the schema, no longer outlive that line: parse() trims the field table back to the declared definitions.

Refs SV-77
@github-actions

github-actions Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

The Robin Hood of code review

✅ Review done. Nothing worth flagging — looks clean to me.

Push fixes whenever you like, then comment /robin for another pass.

Robin v2.12.0 · antongulin/robin@main (fc0bf91) · agent review: 4 turns · 17 tool calls

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Robin Hood of code review

Heads up: this is a point-in-time review. Push fixes freely, then comment /robin whenever you want another pass.

✅ No issues found

Summary

The change cleanly defers field construction and validation while preserving received wire bytes, and it adds useful coverage for lazy reads, malformed values, and round-tripping. I found no actionable correctness or security issues in the reviewed changes.

Segment docblocks now say parse() defers field parsing, reads may throw for malformed fields, and serialize() keeps received bytes. The Unreleased entries share one benchmark baseline and form a single list.

Refs SV-77
@github-actions

github-actions Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

The Robin Hood of code review

✅ Review done. Nothing worth flagging — looks clean to me.

Push fixes whenever you like, then comment /robin for another pass.

Robin v2.12.0 · antongulin/robin@main (fc0bf91) · agent review: 5 turns · 20 tool calls

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Robin Hood of code review

Heads up: this is a point-in-time review. Push fixes freely, then comment /robin whenever you want another pass.

✅ No issues found

Summary

The change adds lazy field parsing and preserves received wire bytes during serialization, with broad tests covering malformed fields, mutation, and round-tripping. The implementation and documentation are generally consistent, and I found no actionable correctness, security, or reliability issues in the reviewed diff.

@shadowhand
shadowhand merged commit 8fd96b8 into main Oct 17, 2026
2 checks passed
@shadowhand
shadowhand deleted the feature/sv-77-defer-segment-field-parsing-until-first-access-lazy-fields branch October 17, 2026 13:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant