anchor.diff:              'undefined' -> 'undefined';
               _ -> iolist_to_binary(ArgsData)
           end,
  -    call_server({rpc, PiqiMod, Name, BinData}).
  +    Request = encode_rpc_request(Name, BinData),
  +    ResponseBin = call_server({rpc, PiqiMod, Request}),
  +    piqi_rpc_piqi:parse_response(ResponseBin).
  +
  +
  +encode_rpc_request(FuncName, ArgsData) ->
  +    Request = #piqi_rpc_request{ name = FuncName, data = ArgsData },
  +    RequestIolist = piqi_rpc_piqi:gen_request(Request),
  +    % make sure that we send binary to the server to avoid implicit term
  +    % serialization/deserialization
  +    iolist_to_binary(RequestIolist).
   
   
   -spec rpc/2 :: (
  
positive.code:     end.
  
  
  -spec convert/4 :: (
      TypeName :: string() | binary(),
      InputFormat :: piqi_tools_format(),
      OutputFormat :: piqi_tools_format(),
      Data :: binary() ) -> {ok, Data :: binary()} | {error, string()}.
  
  % convert `Data` of type `TypeName` from `InputFormat` to `OutputFormat`
  convert(TypeName, InputFormat, OutputFormat, Data) ->
      convert('undefined', TypeName, InputFormat, OutputFormat, Data).
  
  
  handle_common_result({rpc_error, _} = X) ->
      % recoverable protocol-level error
      throw({?PIQI_TOOLS_ERROR, X}).
  
negative.code: 
  
  start_common(StartFun) ->
      gen_server:StartFun({local, ?SERVER}, ?MODULE, [], []).
  
  
  % manual stop (when started by start/0 *)
  stop() ->
      gen_server:cast(?SERVER, stop).
  
  
  %
  % gen_server callbacks
  %
  
  %% @private
  init([]) ->
      Command = "piqi server" ++ ?PIQI_FLAGS,
      %Command = "tee ilog | piqi server --trace | tee olog",
  
      Port = start_port_receiver(Command),
      State = #sender_state{ port = Port },
      {ok, State}.
  
  
  %% @private
  handle_call({rpc, PiqiMod, Request}, From, State = #sender_state{port = Port}) ->
      rpc_call(PiqiMod, Request, From, Port),
      {noreply, State}.
  
  
  %% @private user-initiated stop
  handle_cast(stop, State) ->
      % NOTE: linked port receiver will be shut down automatically
      {stop, normal, State}.
  
  
  handle_info(Info, State) ->
      StopReason = {?PIQI_TOOLS_ERROR, {'unexpected_info', Info}},
      {stop, StopReason, State}.
  
  
  %% @private
  terminate(_Reason, _State = #sender_state{port = Port}) ->
      % don't bother checking if the port is still valid, e.g. after EXIT
      catch erlang:port_close(Port),
      ok.
  
  
  %% @private
  code_change(_OldVsn, State, _Extra) ->
      {ok, State}.
  
  
  % start a new port receiver process
  start_port_receiver(Command) ->
      _Pid = spawn_link(?MODULE, port_receiver, [self(), Command]),
      receive
          {'started', Port} -> Port
      after ?PORT_START_TIMEOUT ->
          exit({?PIQI_TOOLS_ERROR, 'open_port_timeout'})
      end.
  
  
  % entry point of the process responsible for receiving RPC responses from the
  % erlang Port
  port_receiver(Parent, Command) ->
      erlang:process_flag(trap_exit, true),
  
      % TODO: handle initialization error and report it as {error, Error} tuple
      % TODO, XXX: on error, sleep for a while to prevent an immediate restart
      % attempt
      % XXX: use {spawn_executable, Command} instead to avoid shell's error
      % messages printed on stderr?
  
      % XXX: what about closing the port?
      %erlang:port_close(State#receiver_state.port),
  
      Port = erlang:open_port({spawn, Command}, [{packet, 4}, binary]), % exit_status?
      Parent ! {'started', Port},
  
      State = #receiver_state{ port = Port, parent = Parent },
      port_receive_loop(State).
  
  
  port_receive_loop(State = #receiver_state{port = Port, parent = Parent}) ->
      receive
          Message ->
              case Message of
                  {Port, {data, Packet}} ->
                      NewState = handle_rpc_response(Packet, State),
                      port_receive_loop(NewState);
                  {'EXIT', Port, Reason} ->
                      StopReason = {?PIQI_TOOLS_ERROR, {'port_command_exited', Reason}},
                      exit(StopReason);
                  {'EXIT', Parent, _Reason} ->
                      exit(normal);
                  _ ->
                      StopReason = {?PIQI_TOOLS_ERROR, {'unexpected_message', Message}},
                      exit(StopReason)
              end
      end.
  
  
  % do the actual rpc call
  rpc_call(PiqiMod, Request, From, Port) ->
      % NOTE: using process dictionary as a fast SET container to check whether
      % type information from this module has been added to the piqi server
      % XXX: use 'sets' module's sets instead of process dictionary?
      case PiqiMod =:= 'undefined' orelse get(PiqiMod) =:= 'add_piqi' of
          false ->
              % add type information to the Piqi server from the module before
              % calling the actual server function
  