+ cat_async_step($self, $inflight); # take one step
+}
+
+# returns true if prefetch is successful
+sub async_prefetch {
+ my ($self, $oid, $cb, $arg) = @_;
+ my $inflight = $self->{inflight} or return;
+ return if @$inflight;
+ substr($oid, 0, 0) = 'contents ' if $self->{-bc};
+ write_all($self, $self->{out}, "$oid\n", \&cat_async_step, $inflight);
+ push(@$inflight, $oid, $cb, $arg);
+}
+
+sub cat_async_step ($$) {
+ my ($self, $inflight) = @_;
+ die 'BUG: inflight empty or odd' if scalar(@$inflight) < 3;
+ my ($req, $cb, $arg) = @$inflight[0, 1, 2];
+ my $rbuf = delete($self->{rbuf}) // \(my $new = '');
+ my ($bref, $oid, $type, $size);
+ my $head = my_readline($self->{in}, $rbuf);
+ my $cmd = ref($req) ? $$req : $req;
+ # ->fail may be called via Gcf2Client.pm
+ my $bc = $self->{-bc};
+ if ($head =~ /^([0-9a-f]{40,}) (\S+) ([0-9]+)$/) {
+ ($oid, $type, $size) = ($1, $2, $3 + 0);
+ unless ($bc && $cmd =~ /\Ainfo /) { # --batch-command
+ $bref = my_read($self->{in}, $rbuf, $size + 1) or
+ $self->fail(defined($bref) ?
+ 'read EOF' : "read: $!");
+ chop($$bref) eq "\n" or
+ $self->fail('LF missing after blob');
+ }
+ } elsif ($bc && $cmd =~ /\Ainfo / &&
+ $head =~ / (missing|ambiguous)\n/) {
+ $type = $1;
+ $oid = substr($cmd, 5);
+ } elsif ($head =~ s/ missing\n//s) {
+ $oid = $head;
+ # ref($req) indicates it's already been retried
+ # -gcf2 retries internally, so it never hits this path:
+ if (!ref($req) && !$in_cleanup && $self->alternates_changed) {
+ return cat_async_retry($self, $inflight);
+ }
+ $type = 'missing';
+ if ($oid eq '') {
+ $oid = $cmd;
+ $oid =~ s/\A(?:contents|info) // if $bc;
+ }
+ } else {
+ my $err = $! ? " ($!)" : '';
+ $self->fail("bad result from async cat-file: $head$err");
+ }
+ $self->{rbuf} = $rbuf if $$rbuf ne '';
+ splice(@$inflight, 0, 3); # don't retry $cb on ->fail
+ if ($bc && $cmd =~ /\Ainfo /) {
+ eval { $cb->($oid, $type, $size, $arg, $self) };
+ async_err($self, $req, $oid, $@, 'check') if $@;
+ } else {
+ eval { $cb->($bref, $oid, $type, $size, $arg) };
+ async_err($self, $req, $oid, $@, 'cat') if $@;
+ }