bitsery icon indicating copy to clipboard operation
bitsery copied to clipboard

Problem with deserialization of structure - Assertion newOffset <= _endReadOffset

Open hanusek opened this issue 3 years ago • 1 comments
trafficstars

What's your question?

How to fix it?

Additional Context

Hello. I have a problem with the deserialization of my structure. (Assertion newOffset <= _endReadOffset)

I have two strings (wrapped by Text4b) in the struct:

struct AuthFrameResponse
{
    MessageType msg_type;
    Text4b refresh_token;
    Text4b access_token;
}
struct Text4b
{
   uint32_t content_size{0};
   std::string content;
   static constexpr uint32_t MaxSize = std::numeric_limits<uint32_t>::max();
      
   template <typename S>
   void serialize(S &s)
   {
          if(content.size() > MaxSize)
          {
            throw std::runtime_error("content is too long");
          }

          s.value4b(content_size);          
          content.resize(content_size);

          for(uint i=0; i<content_size; i++)
          {
              char c; 
              s.value1b(c);
              content.push_back(c);
          }
    }
};

Code:

struct BitseryConfig
{
    static constexpr bitsery::EndiannessType Endianness = bitsery::DefaultConfig::Endianness;
    static constexpr bool CheckAdapterErrors            = false;
    static constexpr bool CheckDataErrors               = false;

}; // struct BitseryConfig

template <class T>
bool fromBitseryBinary(std::vector<uint8_t> &bytes, T &t)
{
    using InputAdapter = bitsery::InputBufferAdapter<std::vector<uint8_t>, BitseryConfig>;

    auto state = bitsery::quickDeserialization<InputAdapter, T>(InputAdapter(std::begin(bytes), bytes.size()), t);
    return state.first == bitsery::ReaderError::NoError;
}

namespace AuthProtocol 
{
    enum class MessageType : uint32_t 
    { 
      RES  = 0,
      AUTH_INIT,
      AUTH_ERROR,
      UPDATE_ACCESS_TOKEN_REQUEST,
      UPDATE_ACCESS_TOKEN_RESPONSE,
      CMD
    };
    
    struct Text4b
    {
      uint32_t content_size{0};
      std::string content;
      static constexpr uint32_t MaxSize = std::numeric_limits<uint32_t>::max();
      
      template <typename S>
      void serialize(S &s)
      {
          if(content.size() > MaxSize)
          {
            throw std::runtime_error("content is too long");
          }

          s.value4b(content_size);          
          content.resize(content_size);

          for(uint i=0; i<content_size; i++)
          {
              char c; 
              s.value1b(c);
              content.push_back(c);
          }
      }
    };

    struct AuthFrameResponse
    {
        MessageType t;
        Text4b refresh_token;
        Text4b access_token;

        template <typename S>
        void serialize(S &s)
        {
            s.value4b(t);
            s.object(refresh_token);
            s.object(access_token);
        }
    };
}// namespace

///... read via tcp

   boost::asio::async_read(_socket, boost::asio::buffer(_recv_data), boost::asio::transfer_at_least(512), [&] (boost::system::error_code error, std::size_t bytes_transferred)
    {            
        if (error)
        {        
            std::cout << "RECV ERR message: " << error.message() << std::endl;
        }
        else if(bytes_transferred != 0)
        {   
            std::cout << "receive_auth_response bytes_transferred: " << bytes_transferred << std::endl;
                AuthProtocol::AuthFrameResponse response;
                if (!fromBitseryBinary(_recv_data, response))
                {
                    std::cout << "fromBitseryBinary error"  << std::endl;
                    return;
                }
          }
    });

Error:

receive_auth_response bytes_transferred: 722
/home/mhanusek/.conan/data/bitsery/5.1.0/_/_/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/include/bitsery/adapter/buffer.h:120: void bitsery::InputBufferAdapter<Buffer, Config>::readInternalValueChecked(bitsery::InputBufferAdapter<Buffer, Config>::TValue*, std::false_type) [with long unsigned int SIZE = 1; Buffer = std::vector<unsigned char>; Config = BitseryConfig; bitsery::InputBufferAdapter<Buffer, Config>::TValue = unsigned char; std::false_type = std::integral_constant<bool, false>]: Assertion `newOffset <= _endReadOffset' failed.

hanusek avatar Sep 30 '22 12:09 hanusek

Hello,

There might be two things: First:

          s.value4b(content_size);          
          content.resize(content_size);

          for(uint i=0; i<content_size; i++)
          {
              char c; 
              s.value1b(c);
              content.push_back(c);
          }

is not correct. I think you wanted to write content[i] = c; instead of content.push(), but also I think that you might be able to delete content_size and simply use content.size() instead. That would become as simple as:

    s.text1b(content);

Second, I'm not sure, but maybe the problem is with boost::asio::buffer(_recv_data). Maybe you expect to deserialize 1000bytes, but actually receive only 500?. TCP is streaming protocol, which means that it can split one message into multiple pieces. I would suggest to:

  1. read "header" first, which would contain size of whole message (AuthFrameResponse).
  2. then read into separate buffer until you have all the bytes for the message.
  3. finally deserialize the message.

Hope that helps.

fraillt avatar Sep 30 '22 18:09 fraillt