Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Need clarification on sf::Packet << char * operator  (Read 3017 times)

0 Members and 1 Guest are viewing this topic.

Pindrought

  • Newbie
  • *
  • Posts: 11
    • View Profile
Need clarification on sf::Packet << char * operator
« on: March 27, 2018, 04:26:11 pm »
I am trying to understand how something works in the SFML library and replicate it outside of the library for learning purposes.

With this operator
Code: [Select]
Packet & operator<< (const char *data)
Does it assume that the data is a null terminated string or can this be used for a buffer of data from a binary file?

I was under the impression that the only way to append raw chunks of data would be to use something like
Code: [Select]
void append (const void *data, std::size_t sizeInBytes)
but I just wanted to get clarification. Thanks so much.


Edit: If the append function is meant to be used, how would a chunk of data be retrieved in the same manner for an incoming packet?

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 10835
    • View Profile
    • development blog
    • Email
Re: Need clarification on sf::Packet << char * operator
« Reply #1 on: March 27, 2018, 05:14:07 pm »
The easiest is to just check the source code, it's very simple to read and understand: https://github.com/SFML/SFML/blob/master/src/SFML/Network/Packet.cpp#L494

And yes it's about null-terminated strings.
Use the overload you mentioned for sending binary data.
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

Pindrought

  • Newbie
  • *
  • Posts: 11
    • View Profile
Re: Need clarification on sf::Packet << char * operator
« Reply #2 on: March 28, 2018, 01:29:42 am »
The easiest is to just check the source code, it's very simple to read and understand: https://github.com/SFML/SFML/blob/master/src/SFML/Network/Packet.cpp#L494

And yes it's about null-terminated strings.
Use the overload you mentioned for sending binary data.

Thank you this was very helpful. I wasn't sure if I should make a new thread or use this same thread, but I had a question regarding the source code.

In the following code, is it not problematic that the code assumes that the processor stores the integer in little endian format and needs to convert the integer from big endian (network byte order) to little endian (assumed machine byte order)?

Code: [Select]
Packet& Packet::operator >>(Uint64& data)
{
    if (checkSize(sizeof(data)))
    {
        // Since ntohll is not available everywhere, we have to convert
        // to network byte order (big endian) manually
        const Uint8* bytes = reinterpret_cast<const Uint8*>(&m_data[m_readPos]);
        data = (static_cast<Uint64>(bytes[0]) << 56) |
               (static_cast<Uint64>(bytes[1]) << 48) |
               (static_cast<Uint64>(bytes[2]) << 40) |
               (static_cast<Uint64>(bytes[3]) << 32) |
               (static_cast<Uint64>(bytes[4]) << 24) |
               (static_cast<Uint64>(bytes[5]) << 16) |
               (static_cast<Uint64>(bytes[6]) <<  8) |
               (static_cast<Uint64>(bytes[7])      );
        m_readPos += sizeof(data);
    }

    return *this;
}

Similarly, when appending an 8 byte integer, this same idea is applied. Is it not an issue that the code is just assuming that the machine stores integers in little endian format and will need to convert them to big endian or is there just something I am not understanding?

Code: [Select]
Packet& Packet::operator <<(Uint64 data)
{
    // Since htonll is not available everywhere, we have to convert
    // to network byte order (big endian) manually
    Uint8 toWrite[] =
    {
        static_cast<Uint8>((data >> 56) & 0xFF),
        static_cast<Uint8>((data >> 48) & 0xFF),
        static_cast<Uint8>((data >> 40) & 0xFF),
        static_cast<Uint8>((data >> 32) & 0xFF),
        static_cast<Uint8>((data >> 24) & 0xFF),
        static_cast<Uint8>((data >> 16) & 0xFF),
        static_cast<Uint8>((data >>  8) & 0xFF),
        static_cast<Uint8>((data      ) & 0xFF)
    };
    append(&toWrite, sizeof(toWrite));
    return *this;
}

Edited to fix errors in post.
« Last Edit: March 28, 2018, 01:32:03 am by Pindrought »

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: Need clarification on sf::Packet << char * operator
« Reply #3 on: March 28, 2018, 07:54:59 am »
This code doesn't assume anything and works perfectly with any byte order. Bit shift and masking work regardless of the memory layout; you start to get dependant of the byte order when you try to interpret things as raw bytes, which we don't do here.
Laurent Gomila - SFML developer

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: Need clarification on sf::Packet << char * operator
« Reply #4 on: March 28, 2018, 03:33:55 pm »
On little endian machine:
Quote
(data >> 56) is the most significant byte, we put it in first position of the byte array so we get MSB-first which is big endian.
Since we're on little endian, it was originally at the end of the memory space used by "data", so we're actually swapping

On big endian machine:
Quote
(data >> 56) is the most significant byte, we put it in first position of the byte array so we get MSB-first which is big endian.
Since we're on big endian, it was originally at the beginning of the memory space used by "data", so we're actually not changing anything

What you missed is that it's the compiler which already interprets the byte order to give us the MSB when we do (data >> 56).
Laurent Gomila - SFML developer

Pindrought

  • Newbie
  • *
  • Posts: 11
    • View Profile
Re: Need clarification on sf::Packet << char * operator
« Reply #5 on: March 28, 2018, 03:40:14 pm »
On little endian machine:
Quote
(data >> 56) is the most significant byte, we put it in first position of the byte array so we get MSB-first which is big endian.
Since we're on little endian, it was originally at the end of the memory space used by "data", so we're actually swapping

On big endian machine:
Quote
(data >> 56) is the most significant byte, we put it in first position of the byte array so we get MSB-first which is big endian.
Since we're on big endian, it was originally at the beginning of the memory space used by "data", so we're actually not changing anything

What you missed is that it's the compiler which already interprets the byte order to give us the MSB when we do (data >> 56).

Ok this makes way more sense. Thanks. I was misinterpreting the code and assuming that the bytes were always being reversed.