+sub read_cat_in_full ($$) {
+ my ($self, $len) = @_;
+ ++$len; # for final "\n" added by git
+ read($self->{in}, my $buf, $len) == $len or fail($self, 'short read');
+ chop($buf) eq "\n" or fail($self, 'newline missing after blob');
+ \$buf;
+}
+
+sub _cat_async_step ($$) {
+ my ($self, $inflight) = @_;
+ my $pair = shift @$inflight or die 'BUG: inflight empty';
+ my ($cb, $arg) = @$pair;
+ local $/ = "\n";
+ my $head = readline($self->{in});
+ $head =~ / missing$/ and return
+ eval { $cb->(undef, undef, undef, undef, $arg) };
+
+ $head =~ /^([0-9a-f]{40}) (\S+) ([0-9]+)$/ or
+ fail($self, "Unexpected result from async git cat-file: $head");
+ my ($oid_hex, $type, $size) = ($1, $2, $3 + 0);
+ my $bref = read_cat_in_full($self, $size);
+ eval { $cb->($bref, $oid_hex, $type, $size, $arg) };
+}
+
+sub cat_async_wait ($) {
+ my ($self) = @_;
+ my $inflight = delete $self->{inflight} or return;
+ while (scalar(@$inflight)) {
+ _cat_async_step($self, $inflight);
+ }
+}
+