in runtime/under-io-module.cpp [310:415]
static RawObject readBig(Thread* thread, const BufferedReader& buffered_reader,
word num_bytes) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
word available = buffered_reader.bufferNumBytes() - buffered_reader.readPos();
DCHECK(num_bytes == kMaxWord || num_bytes > available,
"num_bytes should be big");
// TODO(T59000373): We could specialize this to avoid the intermediate
// allocations when the size of the result is known and `readinto` is
// available.
word length = available;
Object chunks(&scope, NoneType::object());
Object chunk(&scope, NoneType::object());
Object raw_file(&scope, buffered_reader.underlying());
Bytes bytes(&scope, Bytes::empty());
for (;;) {
word wanted = (num_bytes == kMaxWord) ? 32 * kKiB : num_bytes - available;
Object wanted_int(&scope, SmallInt::fromWord(wanted));
Object result_obj(&scope,
thread->invokeMethod2(raw_file, ID(read), wanted_int));
if (result_obj.isError()) {
if (result_obj.isErrorException()) return *result_obj;
if (result_obj.isErrorNotFound()) {
if (raw_file.isNoneType()) {
return thread->raiseWithFmt(LayoutId::kValueError,
"raw stream has been detached");
}
Object name(&scope, runtime->symbols()->at(ID(read)));
return objectRaiseAttributeError(thread, raw_file, name);
}
}
if (result_obj.isNoneType()) {
if (length == 0) return NoneType::object();
break;
}
word chunk_length;
if (runtime->isInstanceOfBytes(*result_obj)) {
bytes = bytesUnderlying(*result_obj);
chunk = *bytes;
chunk_length = bytes.length();
} else if (runtime->isInstanceOfBytearray(*result_obj)) {
Bytearray byte_array(&scope, *result_obj);
bytes = byte_array.items();
chunk = *byte_array;
chunk_length = byte_array.numItems();
} else if (runtime->isByteslike(*result_obj)) {
UNIMPLEMENTED("byteslike");
} else {
return thread->raiseWithFmt(LayoutId::kTypeError,
"read() should return bytes");
}
if (chunk_length == 0) {
if (length == 0) return *chunk;
break;
}
if (chunk_length > wanted) {
UNIMPLEMENTED("read() returned too many bytes");
}
if (chunks.isNoneType()) {
chunks = runtime->newList();
}
List list(&scope, *chunks);
runtime->listAdd(thread, list, chunk);
length += chunk_length;
if (num_bytes != kMaxWord) {
num_bytes -= chunk_length;
if (num_bytes <= 0) break;
}
}
MutableBytes result(&scope, runtime->newMutableBytesUninitialized(length));
word idx = 0;
if (available > 0) {
result.replaceFromWithStartAt(idx,
MutableBytes::cast(buffered_reader.readBuf()),
available, buffered_reader.readPos());
idx += available;
buffered_reader.setReadPos(0);
buffered_reader.setBufferNumBytes(0);
}
if (!chunks.isNoneType()) {
List list(&scope, *chunks);
for (word i = 0, num_items = list.numItems(); i < num_items; i++) {
chunk = list.at(i);
word chunk_length;
if (chunk.isBytes()) {
bytes = *chunk;
chunk_length = bytes.length();
} else {
Bytearray byte_array(&scope, *chunk);
bytes = byte_array.items();
chunk_length = byte_array.numItems();
}
result.replaceFromWithBytes(idx, *bytes, chunk_length);
idx += chunk_length;
}
}
DCHECK(idx == length, "mismatched length");
return result.becomeImmutable();
}