File Coverage

lib/Net/BitTorrent/Torrent/PiecePicker.pm
Criterion Covered Total %
statement 17 17 100.0
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 23 23 100.0


line stmt bran cond sub pod time code
1 20     20   246 use v5.40;
  20         70  
2 20     20   115 use feature 'class';
  20         37  
  20         2496  
3 20     20   117 no warnings 'experimental::class';
  20         42  
  20         1164  
4             #
5 20     20   119 use Net::BitTorrent::Emitter;
  20         82  
  20         2319  
6             class Net::BitTorrent::Torrent::PiecePicker v2.0.0 : isa(Net::BitTorrent::Emitter) {
7 20     20   9611 use Acme::Selection::RarestFirst;
  20         21004  
  20         925  
8 20     20   9174 use Net::BitTorrent::Types qw[:pick];
  20         66  
  20         42291  
9             field $bitfield : param;
10             field $rarest_first = Acme::Selection::RarestFirst->new( size => $bitfield->size );
11             field $piece_priorities : param = undef;
12             field @piece_priorities;
13             field $strategy : param : reader : writer = PICK_RAREST_FIRST; # SEQUENTIAL, RAREST_FIRST, STREAMING
14             field $end_game : reader = 0;
15             #
16             ADJUST {
17             if ($piece_priorities) {
18             @piece_priorities = @$piece_priorities;
19             }
20             else {
21             @piece_priorities = (1) x $bitfield->size;
22             }
23             }
24             method update_availability ( $peer_bitfield, $delta ) { $rarest_first->update( $peer_bitfield, $delta ) }
25              
26             method set_priority ( $index, $priority ) {
27             return if $index < 0 || $index >= $bitfield->size;
28             $piece_priorities[$index] = $priority;
29             }
30             method get_priority ($index) { $piece_priorities[$index] // 1 }
31             method get_availability ($index) { $rarest_first->get_availability($index) }
32              
33             method is_interesting ($peer) {
34             my $p_bf = $peer->torrent->peer_bitfields->{$peer};
35             unless ($p_bf) {
36             return 0;
37             }
38             for ( my $i = 0; $i < $bitfield->size; $i++ ) {
39             if ( $p_bf->get($i) && !$bitfield->get($i) && $piece_priorities[$i] > 0 ) {
40             return 1;
41             }
42             }
43             return 0;
44             }
45              
46             method pick_piece ( $peer_bitfield, $blocks_pending ) {
47             return undef unless $peer_bitfield;
48              
49             # Get all candidates
50             my @candidates;
51             for ( my $i = 0; $i < $bitfield->size; $i++ ) {
52             next if $bitfield->get($i);
53             next if !$peer_bitfield->get($i);
54             next if $piece_priorities[$i] <= 0;
55             push @candidates, $i;
56             }
57             return undef unless @candidates;
58              
59             # Apply Strategy to candidates
60             if ( $strategy == PICK_SEQUENTIAL ) {
61              
62             # Already sorted by index
63             }
64             elsif ( $strategy == PICK_STREAMING ) {
65             @candidates = sort { ( $piece_priorities[$b] <=> $piece_priorities[$a] ) || ( $a <=> $b ) } @candidates;
66             }
67             else {
68             # RAREST_FIRST
69             @candidates = sort { $rarest_first->get_availability($a) <=> $rarest_first->get_availability($b) } @candidates;
70             }
71             return $candidates[0];
72             }
73              
74             method pick_block ( $peer, $blocks_pending ) {
75             my $peer_bitfield = $peer->torrent->peer_bitfields->{$peer};
76             return undef unless $peer_bitfield;
77              
78             # Get all candidates
79             my @candidates;
80             for ( my $i = 0; $i < $bitfield->size; $i++ ) {
81             next if $bitfield->get($i);
82             next if !$peer_bitfield->get($i);
83             next if $piece_priorities[$i] <= 0;
84             push @candidates, $i;
85             }
86             return undef unless @candidates;
87              
88             # Apply Strategy to candidates
89             if ( $strategy == PICK_SEQUENTIAL ) {
90              
91             # Already sorted by index
92             }
93             elsif ( $strategy == PICK_STREAMING ) {
94             @candidates = sort { ( $piece_priorities[$b] <=> $piece_priorities[$a] ) || ( $a <=> $b ) } @candidates;
95             }
96             else {
97             # RAREST_FIRST
98             @candidates = sort { $rarest_first->get_availability($a) <=> $rarest_first->get_availability($b) } @candidates;
99             }
100             for my $piece_idx (@candidates) {
101             my $piece_len = $peer->torrent->piece_length($piece_idx);
102             my $offset = 0;
103             my $blocks_received = $peer->torrent->blocks_received;
104             while ( $offset < $piece_len ) {
105             my $is_pending = $blocks_pending->{$piece_idx} && $blocks_pending->{$piece_idx}{$offset};
106             my $is_received = $blocks_received->{$piece_idx} && $blocks_received->{$piece_idx}{$offset};
107             if ( $end_game || ( !$is_pending && !$is_received ) ) {
108             if ( !$is_received ) {
109             return ( $piece_idx, $offset, 16384 );
110             }
111             }
112             $offset += 16384;
113             }
114             }
115             return undef;
116             }
117             method enter_end_game () { $end_game = 1 }
118             }
119             #
120             1;