Skip to content

Commit 101d9b6

Browse files
alphagoccclaude
andcommitted
docs(judge): document BufferedStreamReader chunked-read contract
Make explicit that nextUntilNewLine() / nextUntilSpace() return at most 32 chars per call as a fixed-capacity-string optimization, not as a line-truncation -- callers loop until eof() to consume the whole line/token. Also note the primed-buffer trick in the ctor and the EOF latch in peekChar(), and clarify tryNextLine()'s idempotence. Comments only; no behavior change. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> (cherry picked from commit d8a887f)
1 parent 5a66132 commit 101d9b6

1 file changed

Lines changed: 23 additions & 0 deletions

File tree

src/core/judgingthread.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,21 @@ auto JudgingThread::getNeedRejudge() const -> bool { return needRejudge; }
9898

9999
void 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.
101108
class 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.
153167
void 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.
165184
auto 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.
178201
auto BufferedStreamReader::nextUntilSpace() -> std::string_view {
179202
result.clear();
180203
for (char c = peekChar(); std::isspace(c) && ! eof(); c = peekChar())

0 commit comments

Comments
 (0)