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:
1 parent
94d4a30e88
commit
a033cc6718
13 files changed
+443
-136
No files matched your search
@@ -1,70 +1,80 @@
|
||||
#ifndef __Parser_h__
|
||||
#define __Parser_h__
|
||||
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include "StreamReader.h"
|
||||
|
||||
namespace coreutils {
|
||||
|
||||
///
|
||||
/// Use the StreamReader to read data from a socket and buffer it for use by
|
||||
/// various stream parsers such as IMFMessage. The reader maintains data
|
||||
/// consistency regardless of the packet delivery for the stream.
|
||||
/// Parser is the base class for building a protocol/object parser that reads
|
||||
/// from a shared StreamReader's buffer. A subclass implements parse() as a
|
||||
/// resumable state machine (typically a switch on an internal state member)
|
||||
/// that consumes as much of the buffer as it currently can and reports how
|
||||
/// the parse should proceed via Status:
|
||||
///
|
||||
/// - COMPLETE: the object this Parser builds is fully parsed and ready.
|
||||
/// - INCOMPLETE: not enough data is currently buffered to make more
|
||||
/// progress; the caller should wait for more data
|
||||
/// (StreamReader::feed()) before calling doParse() again.
|
||||
/// The subclass's state member must reflect exactly where
|
||||
/// parsing should resume - and must NOT have consumed any
|
||||
/// cursor position it can't yet commit to (see individual
|
||||
/// Parser subclasses for how they guarantee this).
|
||||
/// - DATA_AVAIL: this call made progress and there is more buffered data
|
||||
/// this Parser can still work through right now, without
|
||||
/// waiting for new data to arrive.
|
||||
///
|
||||
/// Call doParse() (not parse() directly) to drive a Parser - it loops on
|
||||
/// DATA_AVAIL automatically and returns once the result is COMPLETE or
|
||||
/// INCOMPLETE.
|
||||
///
|
||||
|
||||
class Parser : public MString {
|
||||
class Parser {
|
||||
|
||||
public:
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
|
||||
Parser(int socket);
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
Parser(StreamReader &reader);
|
||||
|
||||
enum class Status {
|
||||
COMPLETE,
|
||||
INCOMPLETE,
|
||||
DATA_AVAIL
|
||||
COMPLETE,
|
||||
INCOMPLETE,
|
||||
DATA_AVAIL
|
||||
};
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
|
||||
virtual ~Parser();
|
||||
|
||||
///
|
||||
/// doParse is the entry and re-entry point method to continue the parsing process.
|
||||
///
|
||||
|
||||
Status doParse();
|
||||
|
||||
///
|
||||
/// Inside of parse() you can call another parser object with the checkParse method to
|
||||
/// determine if the parsing should branch to the new passed parsing object.
|
||||
/// doParse is the entry and re-entry point method to continue the
|
||||
/// parsing process. Safe to call repeatedly (e.g. once per
|
||||
/// StreamReader::feed()) - each call resumes exactly where the
|
||||
/// previous one left off.
|
||||
///
|
||||
|
||||
bool checkParse(Parser &parser); <--- can be called in parse to see if the parsing should branch
|
||||
Status doParse();
|
||||
|
||||
///
|
||||
/// Inside of parse() you can call another parser object with the
|
||||
/// checkParse method to attempt parsing with a candidate sub-parser
|
||||
/// sharing the same StreamReader. Returns true only once that
|
||||
/// sub-parser fully completes - retain the same sub-parser instance
|
||||
/// across re-entries into your own parse() (e.g. as a member) so it
|
||||
/// can resume, rather than constructing a new one on every call.
|
||||
///
|
||||
///
|
||||
|
||||
virtual Status parse();
|
||||
|
||||
|
||||
bool checkParse(Parser &parser);
|
||||
|
||||
protected:
|
||||
MString buffer;
|
||||
|
||||
private:
|
||||
int socket;
|
||||
Parser *parser;
|
||||
vector<Parser *> chain;
|
||||
|
||||
|
||||
///
|
||||
/// parse() performs one resumable step of parsing against reader's
|
||||
/// buffer. Must be implemented by every concrete Parser to build its
|
||||
/// specific object.
|
||||
///
|
||||
|
||||
virtual Status parse() = 0;
|
||||
|
||||
StreamReader &reader;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user