<commit_before>%%%-------------------------------------------------------------------
%%% File    : etorrent_t_peer_send.erl
%%% Author  : <NAME>
%%% License : See COPYING
%%% Description : Send out events to a foreign socket.
%%%
%%% Created : 27 Jan 2007 by
%%%   <NAME> <<EMAIL>>
%%%-------------------------------------------------------------------
-module(etorrent_t_peer_send).

-include("etorrent_mnesia_table.hrl").

-behaviour(gen_server).

%% API
-export([start_link/3, remote_request/4, cancel/4, choke/1, unchoke/1,
	 local_request/2, not_interested/1, send_have_piece/2, stop/1,
	 bitfield/2, interested/1]).

%% gen_server callbacks
-export([init/1, handle_info/2, terminate/2, code_change/3,
	 handle_call/3, handle_cast/2]).

-record(state, {socket = none,
	        request_queue = none,

		choke = true,
		interested = false, % Are we interested in the peer?
		timer = none,
		parent = none,
	        piece_cache = none,
		torrent_id = none,
	        file_system_pid = none}).

-define(DEFAULT_KEEP_ALIVE_INTERVAL, 120*1000). % From proto. spec.
-define(MAX_REQUESTS, 1024). % Maximal number of requests a peer may make.
%%====================================================================
%% API
%%====================================================================
start_link(Socket, FilesystemPid, TorrentId) ->
    gen_server:start_link(?MODULE,
			  [Socket, FilesystemPid, TorrentId, self()], []).

%%--------------------------------------------------------------------
%% Func: remote_request(Pid, Index, Offset, Len)
%% Description: The remote end (ie, the peer) requested a chunk
%%  {Index, Offset, Len}
%%--------------------------------------------------------------------
remote_request(Pid, Index, Offset, Len) ->
    gen_server:cast(Pid, {remote_request, Index, Offset, Len}).

%%--------------------------------------------------------------------
%% Func: local_request(Pid, Index, Offset, Len)
%% Description: We request a piece from the peer: {Index, Offset, Len}
%%--------------------------------------------------------------------
local_request(Pid, {Index, Offset, Size}) ->
    gen_server:cast(Pid, {local_request, {Index, Offset, Size}}).

%%--------------------------------------------------------------------
%% Func: cancel(Pid, Index, Offset, Len)
%% Description: Cancel the {Index, Offset, Len} at the peer.
%%--------------------------------------------------------------------
cancel(Pid, Index, Offset, Len) ->
    gen_server:cast(Pid, {cancel_piece, Index, Offset, Len}).

%%--------------------------------------------------------------------
%% Func: choke(Pid)
%% Description: Choke the peer.
%%--------------------------------------------------------------------
choke(Pid) ->
    gen_server:cast(Pid, choke).

%%--------------------------------------------------------------------
%% Func: unchoke(Pid)
%% Description: Unchoke the peer.
%%--------------------------------------------------------------------
unchoke(Pid) ->
    gen_server:cast(Pid, unchoke).

%%--------------------------------------------------------------------
%% Func: not_interested(Pid)
%% Description: Tell the peer we are not interested in him anymore
%%--------------------------------------------------------------------
not_interested(Pid) ->
    gen_server:cast(Pid, not_interested).

interested(Pid) ->
    gen_server:cast(Pid, interested).

%%--------------------------------------------------------------------
%% Func: send_have_piece(Pid, PieceNumber)
%% Description: Tell the peer we have the piece PieceNumber
%%--------------------------------------------------------------------
send_have_piece(Pid, PieceNumber) ->
    gen_server:cast(Pid, {have, PieceNumber}).

bitfield(Pid, BitField) ->
    gen_server:cast(Pid, {bitfield, BitField}).


%%--------------------------------------------------------------------
%% Func: stop(Pid)
%% Description: Tell the send process to stop the communication.
%%--------------------------------------------------------------------
stop(Pid) ->
    gen_server:cast(Pid, stop).

%%====================================================================
%% gen_server callbacks
%%====================================================================
init([Socket, FilesystemPid, TorrentId, Parent]) ->
    {ok,
     #state{socket = Socket,
	    request_queue = queue:new(),
	    parent = Parent,
	    torrent_id = TorrentId,
	    file_system_pid = FilesystemPid},
     0}.

%%--------------------------------------------------------------------
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
%%                                      {reply, Reply, State, Timeout} |
%%                                      {noreply, State} |
%%                                      {noreply, State, Timeout} |
%%                                      {stop, Reason, Reply, State} |
%%                                      {stop, Reason, State}
%% Description: Handling call messages
%%--------------------------------------------------------------------
handle_call(_Request, _From, State) ->
    Reply = ok,
    {reply, Reply, State}.

%%--------------------------------------------------------------------
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
%% Description: Convert process state when code is changed
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

handle_info(keep_alive_tick, S) ->
    %% Special case. Set the timer and call send_message with a Timout so it avoids
    %% a timer cancel.
    {ok, TRef} = timer:send_after(?DEFAULT_KEEP_ALIVE_INTERVAL, self(), keep_alive_tick),
    send_message(keep_alive, S#state { timer = TRef} , 0);
handle_info(timeout, S) when S#state.choke =:= true ->
    set_timer(S);
handle_info(timeout, S) when S#state.choke =:= false ->
    case queue:out(S#state.request_queue) of
	{empty, _} ->
	    set_timer(S);
	{{value, {Index, Offset, Len}}, NewQ} ->
	    send_piece(Index, Offset, Len, S#state { request_queue = NewQ } )
    end;
handle_info(Msg, S) ->
    error_logger:info_report([got_unknown_message, Msg, S]),
    {stop, {unknown_msg, Msg}}.


handle_cast(choke, S) when S#state.choke == true ->
    {noreply, S, 0};
handle_cast(choke, S) when S#state.choke == false ->
    send_message(choke, S#state{choke = true});
handle_cast(unchoke, S) when S#state.choke == false ->
    {noreply, S, 0};
handle_cast(unchoke, S) when S#state.choke == true ->
    send_message(unchoke, S#state{choke = false,
				  request_queue = queue:new()});
handle_cast({bitfield, BF}, S) ->
    send_message({bitfield, BF}, S);
handle_cast(not_interested, S) when S#state.interested =:= false -><commit_msg>When choking a peer don't store the last chunk in its local piece cache. Cuts down memory usage by *a* *lot*.<commit_after>%%%-------------------------------------------------------------------
%%% File    : etorrent_t_peer_send.erl
%%% Author  : <NAME>
%%% License : See COPYING
%%% Description : Send out events to a foreign socket.
%%%
%%% Created : 27 Jan 2007 by
%%%   <NAME> <<EMAIL>>
%%%-------------------------------------------------------------------
-module(etorrent_t_peer_send).

-include("etorrent_mnesia_table.hrl").

-behaviour(gen_server).

%% API
-export([start_link/3, remote_request/4, cancel/4, choke/1, unchoke/1,
	 local_request/2, not_interested/1, send_have_piece/2, stop/1,
	 bitfield/2, interested/1]).

%% gen_server callbacks
-export([init/1, handle_info/2, terminate/2, code_change/3,
	 handle_call/3, handle_cast/2]).

-record(state, {socket = none,
	        request_queue = none,

		choke = true,
		interested = false, % Are we interested in the peer?
		timer = none,
		parent = none,
	        piece_cache = none,
		torrent_id = none,
	        file_system_pid = none}).

-define(DEFAULT_KEEP_ALIVE_INTERVAL, 120*1000). % From proto. spec.
-define(MAX_REQUESTS, 1024). % Maximal number of requests a peer may make.
%%====================================================================
%% API
%%====================================================================
start_link(Socket, FilesystemPid, TorrentId) ->
    gen_server:start_link(?MODULE,
			  [Socket, FilesystemPid, TorrentId, self()], []).

%%--------------------------------------------------------------------
%% Func: remote_request(Pid, Index, Offset, Len)
%% Description: The remote end (ie, the peer) requested a chunk
%%  {Index, Offset, Len}
%%--------------------------------------------------------------------
remote_request(Pid, Index, Offset, Len) ->
    gen_server:cast(Pid, {remote_request, Index, Offset, Len}).

%%--------------------------------------------------------------------
%% Func: local_request(Pid, Index, Offset, Len)
%% Description: We request a piece from the peer: {Index, Offset, Len}
%%--------------------------------------------------------------------
local_request(Pid, {Index, Offset, Size}) ->
    gen_server:cast(Pid, {local_request, {Index, Offset, Size}}).

%%--------------------------------------------------------------------
%% Func: cancel(Pid, Index, Offset, Len)
%% Description: Cancel the {Index, Offset, Len} at the peer.
%%--------------------------------------------------------------------
cancel(Pid, Index, Offset, Len) ->
    gen_server:cast(Pid, {cancel_piece, Index, Offset, Len}).

%%--------------------------------------------------------------------
%% Func: choke(Pid)
%% Description: Choke the peer.
%%--------------------------------------------------------------------
choke(Pid) ->
    gen_server:cast(Pid, choke).

%%--------------------------------------------------------------------
%% Func: unchoke(Pid)
%% Description: Unchoke the peer.
%%--------------------------------------------------------------------
unchoke(Pid) ->
    gen_server:cast(Pid, unchoke).

%%--------------------------------------------------------------------
%% Func: not_interested(Pid)
%% Description: Tell the peer we are not interested in him anymore
%%--------------------------------------------------------------------
not_interested(Pid) ->
    gen_server:cast(Pid, not_interested).

interested(Pid) ->
    gen_server:cast(Pid, interested).

%%--------------------------------------------------------------------
%% Func: send_have_piece(Pid, PieceNumber)
%% Description: Tell the peer we have the piece PieceNumber
%%--------------------------------------------------------------------
send_have_piece(Pid, PieceNumber) ->
    gen_server:cast(Pid, {have, PieceNumber}).

bitfield(Pid, BitField) ->
    gen_server:cast(Pid, {bitfield, BitField}).


%%--------------------------------------------------------------------
%% Func: stop(Pid)
%% Description: Tell the send process to stop the communication.
%%--------------------------------------------------------------------
stop(Pid) ->
    gen_server:cast(Pid, stop).

%%====================================================================
%% gen_server callbacks
%%====================================================================
init([Socket, FilesystemPid, TorrentId, Parent]) ->
    {ok,
     #state{socket = Socket,
	    request_queue = queue:new(),
	    parent = Parent,
	    torrent_id = TorrentId,
	    file_system_pid = FilesystemPid},
     0}.

%%--------------------------------------------------------------------
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
%%                                      {reply, Reply, State, Timeout} |
%%                                      {noreply, State} |
%%                                      {noreply, State, Timeout} |
%%                                      {stop, Reason, Reply, State} |
%%                                      {stop, Reason, State}
%% Description: Handling call messages
%%--------------------------------------------------------------------
handle_call(_Request, _From, State) ->
    Reply = ok,
    {reply, Reply, State}.

%%--------------------------------------------------------------------
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
%% Description: Convert process state when code is changed
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

handle_info(keep_alive_tick, S) ->
    %% Special case. Set the timer and call send_message with a Timout so it avoids
    %% a timer cancel.
    {ok, TRef} = timer:send_after(?DEFAULT_KEEP_ALIVE_INTERVAL, self(), keep_alive_tick),
    send_message(keep_alive, S#state { timer = TRef} , 0);
handle_info(timeout, S) when S#state.choke =:= true ->
    set_timer(S);
handle_info(timeout, S) when S#state.choke =:= false ->
    case queue:out(S#state.request_queue) of
	{empty, _} ->
	    set_timer(S);
	{{value, {Index, Offset, Len}}, NewQ} ->
	    send_piece(Index, Offset, Len, S#state { request_queue = NewQ } )
    end;
handle_info(Msg, S) ->
    error_logger:info_report([got_unknown_message, Msg, S]),
    {stop, {unknown_msg, Msg}}.


handle_cast(choke, S) when S#state.choke == true ->
    {noreply, S, 0};
handle_cast(choke, S) when S#state.choke == false ->
    send_message(choke, S#state{choke = true, piece_cache = none});
handle_cast(unchoke, S) when S#state.choke == false ->
    {noreply, S, 0};
handle_cast(unchoke, S) when S#state.choke == true ->
    send_message(unchoke, S#state{choke = false,
				  request_queue = queue:new()});
handle_cast({bitfield, BF}, S) ->
    send_message({bitfield, BF}, S);
handle_cast(not_interested, S) when S#state.interested =:= false ->
{'fraction_non_alphanumeric': 0.3745945148923621, 'fraction_numerical': 0.005603066941905043, 'mean_word_length': 1.612673343605547, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 67, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3008516', 'n_tokens_mistral': 3788, 'n_tokens_neox': 3538, 'n_words': 872}