@@ -98,12 +98,21 @@ auto JudgingThread::getNeedRejudge() const -> bool { return needRejudge; }
9898
9999void JudgingThread::stopJudgingSlot () { stopJudging = true ; }
100100
101+ // Chunked file reader used by the line/space comparators below.
102+ //
103+ // nextUntilNewLine() / nextUntilSpace() return at most 32 chars per call --
104+ // they do NOT truncate a long line; callers must loop until eof() to consume
105+ // the whole line/token. The 32-byte cap keeps `result` (a fixed-capacity
106+ // std::string reserved at 32) from reallocating, which is the hot path of
107+ // per-test-case comparison.
101108class BufferedStreamReader {
102109
103110 public:
104111 static const int BUFFER_SIZE = 1 << 18 ; // 128 KiB
105112 explicit BufferedStreamReader (const char *filename) : file(fopen(filename, " r" )) {
106113 result.reserve (32 );
114+ // Start with bufPos == bufEnd so the first peekChar() triggers an
115+ // fread; avoids a separate "primed?" flag.
107116 bufPos = buffer + BUFFER_SIZE;
108117 bufEnd = buffer + BUFFER_SIZE;
109118 lineNumber = 1 ;
@@ -140,6 +149,8 @@ auto BufferedStreamReader::peekChar() -> char {
140149 return ' \0 ' ;
141150 }
142151 if (bufPos == bufEnd) {
152+ // Buffer drained -- refill. fread returning 0 means real EOF; latch it
153+ // so further peekChar() calls short-circuit without touching the FILE*.
143154 bufPos = buffer;
144155 bufEnd = buffer + fread (buffer, 1 , BUFFER_SIZE, file);
145156 if (bufPos == bufEnd) {
@@ -150,6 +161,9 @@ auto BufferedStreamReader::peekChar() -> char {
150161 return *bufPos;
151162}
152163
164+ // Consume one line terminator (\n, \r, or \r\n) if the cursor sits on one,
165+ // and bump lineNumber. Idempotent when not on a terminator -- safe to call
166+ // before each token read.
153167void BufferedStreamReader::tryNextLine () {
154168 char c = peekChar ();
155169 if (c != ' \r ' && c != ' \n ' )
@@ -162,6 +176,11 @@ void BufferedStreamReader::tryNextLine() {
162176 lineNumber++;
163177}
164178
179+ // Read up to 32 chars from the current position, stopping at any line
180+ // terminator or EOF. Does NOT consume the terminator -- the next call to
181+ // tryNextLine() (invoked at the top here) will. Returning a chunk shorter
182+ // than 32 chars is the signal that the caller has reached end-of-line/EOF;
183+ // otherwise the caller must call again to read the rest of a long line.
165184auto BufferedStreamReader::nextUntilNewLine () -> std::string_view {
166185 result.clear ();
167186 tryNextLine ();
@@ -175,6 +194,10 @@ auto BufferedStreamReader::nextUntilNewLine() -> std::string_view {
175194 return result;
176195}
177196
197+ // Skip leading whitespace (advancing lineNumber across newlines), then read
198+ // up to 32 non-space chars. Same chunked-read contract as nextUntilNewLine:
199+ // short return means token end; long tokens must be read across multiple
200+ // calls. Returns an empty view at EOF.
178201auto BufferedStreamReader::nextUntilSpace () -> std::string_view {
179202 result.clear ();
180203 for (char c = peekChar (); std::isspace (c) && ! eof (); c = peekChar ())
0 commit comments