Add StreamReader and rebuild Parser/LineParser/IMFHeader/IMFBoundary on it

Introduces StreamReader to accumulate socket data delivered in arbitrary
chunks into one buffer, so a Parser can build an object (a line, a header
block, a MIME boundary) that spans any number of packets without losing
its place. Parser is redesigned around this: no longer inherits MString
(was redundant with its own buffer member), takes a StreamReader&, and
drives a resumable COMPLETE/INCOMPLETE/DATA_AVAIL state-machine protocol
via doParse(). LineParser, IMFHeader, and IMFBoundary are rewritten as
real, working resumable parsers against the shared buffer instead of the
previous non-compiling stubs (mismatched constructors/return types, a
syntax error, checkParse() never implemented).

IMFFormData is left as the stub it already was, just restored to
compiling against the new IMFHeader signature - fully wiring it up needs
IMFMultipart/IMFText migrated to StreamReader too, out of scope here.

Verified with real runtime tests (not just compilation) covering data
split mid-line, mid-CRLF, mid-header-fold, and mid-boundary-marker across
separate feed() calls, plus StreamReader::compact(). Along the way fixed
a header-value bug (space after ':' wasn't skipped) and a double
cursor-offset subtraction bug in two places caused by forgetting that
ZString::getLength() is already cursor-relative, not the raw buffer size.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Brad ArantandClaude Sonnet 5 committed 2026-08-23 14:25:58 -07:00
1 parent 94d4a30e88
commit a033cc6718
13 files changed
+443 -136

No files matched your search

+42 -23
View File
@@ -1,30 +1,49 @@
#include "IMFBoundary.h"
#include "Exception.h"
#include <unistd.h>
namespace coreutils {
IMFBoundary(StreamReader &reader, ZString boundary) : boundary(boundary) {
}
int IMFBoundary::parse() {
switch(state) {
case PREFIX:
if(buffer.remaining() > 3)
if(reader.nextIf("\r\n--"))
state = BOUNDARY;
case BOUNDARY:
while(buffer.getTokenInclude(""));
if(buffer.eod())
return 2;
else if(buffer.remaining() > 1)
if(buffer.ifNext("--"))
state = LAST_BOUNDARY;
case LAST_BOUNDARY:
return 0;
IMFBoundary::IMFBoundary(StreamReader &reader, ZString boundary) : Parser(reader), boundary(boundary) {}
Parser::Status IMFBoundary::parse() {
MString &buffer = reader.getBuffer();
switch (state) {
case PREFIX: {
MString marker;
marker << "\r\n--";
marker << boundary;
int pos = buffer.find(marker);
if (pos < 0)
return Status::INCOMPLETE;
if (!buffer.ifNext(marker))
throw coreutils::Exception("IMFBoundary: boundary marker vanished between find and ifNext.");
state = TAIL;
return Status::DATA_AVAIL;
}
case TAIL:
// ZString::getLength() is already cursor-relative (bytes remaining
// from the cursor to the end) - do not subtract the cursor offset
// again here.
if (buffer.getLength() < 2)
return Status::INCOMPLETE;
if (buffer.ifNext(ZString("--"))) {
last = true;
return Status::COMPLETE;
}
if (buffer.ifNext(ZString("\r\n"))) {
last = false;
return Status::COMPLETE;
}
throw coreutils::Exception("IMFBoundary: malformed boundary line - expected CRLF or trailing --.");
}
throw coreutils::Exception("IMFBoundary: unreachable state.");
}
}
bool IMFBoundary::isLast() {
return last;
}
}