anchor.diff:  %% {messages, ppspp_messages()}
   %% ].
   
  --spec unpack(binary(), endpoint(), any()) -> {ok, datagram()}.
  -unpack(Datagram, _Peer, _State ) ->
  +-spec unpack(binary(), endpoint()) -> {ok, datagram()}.
  +unpack(Datagram, _Peer) ->
       {Channel, Maybe_Messages} = ppspp_channel:unpack_with_rest(Datagram),
       ?DEBUG("dgram: received on channel ~p~n",
              [ppspp_channel:channel_to_string(Channel)]),
  
positive.code: -export_type([endpoint/0,
                endpoint_option/0,
                datagram/0]).
  
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %% @doc receives datagram from peer_worker, parses & delivers to matching channel
  %% @spec handle_datagram() -> ok
  %% @end
  -spec handle({udp, inet:socket(), inet:ip_address(), inet:port_number(),
                binary()}, any()) -> ok.
  
  handle({udp, Socket, Peer_IP_Address, Peer_Port, Maybe_Datagram}, State) ->
      Channel = ppspp_channel:get_channel(Maybe_Datagram),
      Endpoint = build_endpoint(udp, Socket, Peer_IP_Address, Peer_Port, Channel),
      {ok, Parsed_Datagram} = unpack(Maybe_Datagram, Endpoint, State),
      % NB usually called from spawned process, so return values are ignored
      handle(Parsed_Datagram).
  
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %% @doc translates raw udp packet into a tidy structure for later use
  
negative.code: -endif.
  
  %% api
  -export([handle/1,
           unpack/2,
           pack/1]).
  
  -opaque endpoint() :: {endpoint, list( endpoint_option())}.
  