Algorithm-Kademlia

 view release on metacpan or  search on metacpan

lib/Algorithm/Kademlia.pm  view on Meta::CPAN

        }

        method get ($key_bin) {
            my $entry = $store{$key_bin} // return;
            if ( time() - $entry->time > $ttl ) {
                delete $store{$key_bin};
                return undef;
            }
            $entry;    # Return the full entry now
        }
    };
    class Algorithm::Kademlia::Storage::Entry v1.1.0 {
        field $key       : param : reader;
        field $leechers  : param : reader : writer;
        field $seeds     : param : reader : writer;
        field $time      : param : reader : writer;
        field $value     : param : reader : writer;
        field $publisher : param : reader;
    };
    class Algorithm::Kademlia::Search v1.1.1 {
        field $target_id_bin : param : reader;
        field $k     : param //= 20;
        field $alpha : param //= 3;
        field %nodes;    # id_bin -> { data => ..., queried => 0, responded => 0, failed => 0 }

        method add_candidates (@peers) {
            for my $peer (@peers) {
                my $id = $peer->{id};
                next if $nodes{$id} && ( $nodes{$id}{queried} || $nodes{$id}{failed} );
                $nodes{$id} //= { data => $peer->{data}, queried => 0, responded => 0, failed => 0 };
            }
        }

        method pending_queries () {
            grep { $_->{queried} && !$_->{responded} && !$_->{failed} } values %nodes;
        }

        method next_to_query () {
            my @sorted = sort { ( $a^.$target_id_bin ) cmp( $b^.$target_id_bin ) } keys %nodes;
            my @to_query;
            for my $id (@sorted) {
                next if $nodes{$id}{queried} || $nodes{$id}{failed};
                push @to_query, { id => $id, data => $nodes{$id}{data} };
                $nodes{$id}{queried} = 1;
                last if @to_query >= $alpha;
            }
            @to_query;
        }

        method mark_responded ( $id_bin, @new_peers ) {
            return unless $nodes{$id_bin};
            $nodes{$id_bin}{responded} = 1;
            $self->add_candidates(@new_peers);
        }

        method mark_failed ($id_bin) {
            return unless $nodes{$id_bin};
            $nodes{$id_bin}{failed} = 1;
        }

        method best_results () {
            my @sorted  = sort { ( $a^.$target_id_bin ) cmp( $b^.$target_id_bin ) } grep { $nodes{$_}{responded} } keys %nodes;
            my @results = map  { { id => $_, data => $nodes{$_}{data} } } splice( @sorted, 0, $k );
            @results;
        }

        method is_finished () {
            my @responded = grep { $_->{responded} } values %nodes;
            return 1 if @responded >= $k;
            my @available = grep { !$_->{queried} && !$_->{failed} } values %nodes;
            return 1 if !@available && !$self->pending_queries;
            0;
        }
    };
};
1;



( run in 0.709 second using v1.01-cache-2.11-cpan-39bf76dae61 )