perf(mtconnect): sample chunks parse from bytes, no per-chunk string copies

This commit is contained in:
Joseph Doherty
2026-08-16 04:05:15 -04:00
parent 3795899d10
commit 34f836e03c
3 changed files with 215 additions and 7 deletions
@@ -294,6 +294,113 @@ public sealed class MTConnectStreamsParseTests
fromStream.Observations.ShouldBe(fromString.Observations);
}
// ---------------------------------------------------- the byte overload the /sample pump feeds on
[Theory]
[InlineData(CurrentFixture)]
[InlineData(SampleFixture)]
[InlineData(GapFixture)]
[InlineData(UnavailableFixture)]
public void Parse_from_bytes_matches_the_string_overload_on_every_fixture(string fixture)
{
// The /sample pump hands the parser the chunk's raw bytes; a chunk parsed from bytes must be
// indistinguishable from the same chunk round-tripped through a string.
var fromBytes = MTConnectStreamsParser.Parse(File.ReadAllBytes(fixture));
var fromString = MTConnectStreamsParser.Parse(File.ReadAllText(fixture));
fromBytes.InstanceId.ShouldBe(fromString.InstanceId);
fromBytes.FirstSequence.ShouldBe(fromString.FirstSequence);
fromBytes.NextSequence.ShouldBe(fromString.NextSequence);
fromBytes.Observations.ShouldBe(fromString.Observations);
}
[Theory]
[InlineData("\r\n")]
[InlineData("\n")]
[InlineData(" \t")]
[InlineData("\r\n\r\n")]
public void Parse_from_bytes_tolerates_framing_whitespace_AHEAD_of_the_xml_declaration(string residue)
{
// THE load-bearing leg of the byte path's trim, and the one that is not falsifiable by
// accident: every fixture opens with an <?xml ...?> declaration, and XmlReader rejects even
// one space ahead of one ("no whitespace characters are allowed to appear before it"). The
// string overload trimmed it; a byte path that fed the frame straight to XDocument.Load
// would turn an Agent that writes a blank line after the part headers into a hard parse
// failure on every chunk. (Trailing whitespace and a leading BOM, by contrast, XmlReader
// handles natively — the cases below pin that rather than the trim.)
var bytes = Encoding.UTF8.GetBytes(residue).Concat(File.ReadAllBytes(SampleFixture)).ToArray();
var withResidue = MTConnectStreamsParser.Parse(bytes);
withResidue.NextSequence.ShouldBe(113);
withResidue.Observations.ShouldBe(MTConnectStreamsParser.Parse(File.ReadAllText(SampleFixture)).Observations);
}
[Theory]
[InlineData(CurrentFixture)]
[InlineData(SampleFixture)]
public void Parse_from_bytes_tolerates_a_utf8_byte_order_mark_ahead_of_the_declaration(string fixture)
{
// A chunk lifted out of a multipart frame can carry a BOM; the string path trimmed ''
// for exactly this reason, so the byte path must handle the EF BB BF that decodes to it.
var bytes = new byte[] { 0xEF, 0xBB, 0xBF }.Concat(File.ReadAllBytes(fixture)).ToArray();
var withBom = MTConnectStreamsParser.Parse(bytes);
withBom.Observations.ShouldBe(MTConnectStreamsParser.Parse(File.ReadAllText(fixture)).Observations);
}
[Theory]
[InlineData("\r\n")]
[InlineData("\r\n\r\n")]
[InlineData("\n")]
[InlineData(" \t\r\n")]
public void Parse_from_bytes_tolerates_the_multipart_frames_trailing_line_break(string residue)
{
// The other half of what the string path's Trim covered: the framing's trailing line break.
var bytes = File.ReadAllBytes(SampleFixture).Concat(Encoding.UTF8.GetBytes(residue)).ToArray();
var withResidue = MTConnectStreamsParser.Parse(bytes);
withResidue.NextSequence.ShouldBe(113);
withResidue.Observations.ShouldBe(MTConnectStreamsParser.Parse(File.ReadAllText(SampleFixture)).Observations);
}
[Fact]
public void Parse_from_bytes_tolerates_a_bom_behind_leading_whitespace()
{
// Whitespace THEN a BOM defeats XmlReader's encoding sniffing outright ("Data at the root
// level is invalid"), so the trim has to interleave the two rather than strip a BOM only at
// offset 0 — which is exactly what the string overload's Trim('', ' ', …) set did.
var bytes = " \r\n"u8.ToArray()
.Concat(new byte[] { 0xEF, 0xBB, 0xBF })
.Concat(File.ReadAllBytes(CurrentFixture))
.Concat("\r\n"u8.ToArray())
.ToArray();
MTConnectStreamsParser.Parse(bytes).Observations
.ShouldBe(MTConnectStreamsParser.Parse(File.ReadAllText(CurrentFixture)).Observations);
}
[Theory]
[InlineData(new byte[0])]
[InlineData(new byte[] { (byte)' ', (byte)'\r', (byte)'\n' })]
[InlineData(new byte[] { 0xEF, 0xBB, 0xBF })]
public void Parse_from_bytes_throws_on_an_empty_payload(byte[] bytes)
{
// Same failure family as the string path: never a silently empty result.
var ex = Should.Throw<InvalidDataException>(() => MTConnectStreamsParser.Parse(bytes));
ex.Message.ShouldContain("empty");
}
[Fact]
public void Parse_from_bytes_throws_on_malformed_xml()
{
Should.Throw<InvalidDataException>(
() => MTConnectStreamsParser.Parse("not xml at all"u8.ToArray()));
}
// ------------------------------------------------------------- fails loudly, never silently empty
[Theory]