in runtime/under-io-module.cpp [514:630]
RawObject FUNC(_io, _buffered_reader_read)(Thread* thread, Arguments args) {
// TODO(T58490915): Investigate what thread safety guarantees python has,
// and add locking code as necessary.
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
Object self_obj(&scope, args.get(0));
if (!runtime->isInstanceOfBufferedReader(*self_obj)) {
return thread->raiseRequiresType(self_obj, ID(BufferedReader));
}
BufferedReader self(&scope, *self_obj);
Object num_bytes_obj(&scope, args.get(1));
word num_bytes;
if (num_bytes_obj.isNoneType()) {
num_bytes = kMaxWord;
} else {
// TODO(T59004416) Is there a way to push intFromIndex() towards managed?
Object num_bytes_int_obj(&scope, intFromIndex(thread, num_bytes_obj));
if (num_bytes_int_obj.isErrorException()) return *num_bytes_int_obj;
Int num_bytes_int(&scope, intUnderlying(*num_bytes_int_obj));
if (!num_bytes_int.isSmallInt() && !num_bytes_int.isBool()) {
return thread->raiseWithFmt(
LayoutId::kOverflowError,
"cannot fit value into an index-sized integer");
}
num_bytes = num_bytes_int.asWord();
if (num_bytes == -1) {
num_bytes = kMaxWord;
} else if (num_bytes < 0) {
return thread->raiseWithFmt(LayoutId::kValueError,
"read length must be non-negative or -1");
}
}
word buffer_num_bytes = self.bufferNumBytes();
word read_pos = self.readPos();
word available = buffer_num_bytes - read_pos;
DCHECK(available >= 0, "invalid state");
if (num_bytes <= available) {
word new_read_pos = read_pos + num_bytes;
self.setReadPos(new_read_pos);
Bytes read_buf(&scope, self.readBuf());
return bytesSubseq(thread, read_buf, read_pos, num_bytes);
}
Object raw_file(&scope, self.underlying());
if (num_bytes == kMaxWord) {
Object readall_result(&scope, thread->invokeMethod1(raw_file, ID(readall)));
if (readall_result.isErrorException()) return *readall_result;
if (!readall_result.isErrorNotFound()) {
Bytes bytes(&scope, Bytes::empty());
word bytes_length;
if (readall_result.isNoneType()) {
if (available == 0) return NoneType::object();
bytes_length = 0;
} else if (runtime->isInstanceOfBytes(*readall_result)) {
bytes = bytesUnderlying(*readall_result);
bytes_length = bytes.length();
} else if (runtime->isInstanceOfBytearray(*readall_result)) {
Bytearray byte_array(&scope, *readall_result);
bytes = byte_array.items();
bytes_length = byte_array.numItems();
} else if (runtime->isByteslike(*readall_result)) {
UNIMPLEMENTED("byteslike");
} else {
return thread->raiseWithFmt(LayoutId::kTypeError,
"readall() should return bytes");
}
word length = bytes_length + available;
if (length == 0) return Bytes::empty();
MutableBytes result(&scope,
runtime->newMutableBytesUninitialized(length));
word idx = 0;
if (available > 0) {
result.replaceFromWithStartAt(idx, MutableBytes::cast(self.readBuf()),
available, read_pos);
idx += available;
self.setReadPos(0);
self.setBufferNumBytes(0);
}
if (bytes_length > 0) {
result.replaceFromWithBytes(idx, *bytes, bytes_length);
idx += bytes_length;
}
DCHECK(idx == length, "length mismatch");
return result.becomeImmutable();
}
}
// Use alternate reading code for big requests where buffering would not help.
// (This is also used for the num_bytes==kMaxWord (aka "readall") case when
// the file object does not provide a "readall" method.
word buffer_size = self.bufferSize();
if (num_bytes > (buffer_size / 2)) {
return readBig(thread, self, num_bytes);
}
// Fill buffer until we have enough bytes available.
MutableBytes read_buf(&scope, rewindOrInitReadBuf(thread, self));
buffer_num_bytes = self.bufferNumBytes();
Object fill_result(&scope, NoneType::object());
do {
fill_result = fillBuffer(thread, raw_file, read_buf, &buffer_num_bytes);
if (fill_result.isErrorException()) return *fill_result;
if (!fill_result.isUnbound()) {
if (buffer_num_bytes == 0) return *fill_result;
break;
}
} while (buffer_num_bytes < num_bytes);
word length = Utils::minimum(buffer_num_bytes, num_bytes);
self.setBufferNumBytes(buffer_num_bytes);
self.setReadPos(length);
Bytes read_buf_bytes(&scope, *read_buf);
return bytesSubseq(thread, read_buf_bytes, 0, length);
}