Big endian -> little endian



  • Und beachten, dass wchar_t nicht überall gleich groß sein muss.



  • Ich wusste doch, daß ich da was hab:

    #ifndef UNION_ULU
    #define UNION_ULU
    union ULU					// union for ip address
    {
    	unsigned char c[4];		// hier: .c[3] = 0xde, .c[2] = 0xad, .c[1] = 0xbe, .c[0] = 0xef
    	unsigned short s[2];	// big endian: .s[0] = 0xdead - hier: little endian: .s[0] = 0xbeef
    	unsigned long l;		// Eingabe: .l = 0xdeadbeef
    };
    #endif // UNION_ULU ///:~
    


  • std::reverse?



  • issen1 schrieb:

    std::reverse?

    Nein!



  • nö++ schrieb:

    issen1 schrieb:

    std::reverse?

    Nein!

    wieso nicht? hatte sich jz nicht so verkehrt angehört...

    bb



  • Irgendwie so vielleicht?

    template<typename T>
    T endianSwap(T val)
    {
        unsigned char const * inPtr = reinterpret_cast<unsigned char const *>(&val);    
        T swapped;
        unsigned char * swappedPtr = reinterpret_cast<unsigned char * >(&swapped);    
        std::reverse_copy
            ( inPtr
            , inPtr + sizeof(val)
            , swappedPtr );
        return swapped;
    }
    


  • Meine union ist doch (ausnahmsweise) gut kommentiert. Da sollte es doch klar sein wie's funktioniert.

    Ich denke nicht, daß reverse funktioniert (hab aber keine Zeit, das jetzt zu testen).

    Einfach nach deadbeef googlen.



  • big endianer schrieb:

    Hallo,
    gibts ne Funktion für die Umwandlung eines Strings im Big Endian Format nach Little Endian ?

    C++ kennt nicht "big endian" oder "little endian". Wie Objekte im Speicher liegen, ist Sache der Implementierung. Du solltest versuchen, all die Situationen zu vermeiden, welche Wissen über die Implementierung erfordern.

    Übrigends: Unter Linux ist ein wchar_t 32Bit groß.
    Übrigends: Es gibt auch Mischformen (mixed endian).

    @EOP:
    Type-punning per union führt laut Standard zu undefiniertem Verhalten. Das, was Du da andeutest funktioniert nur bei bestimmten Compilern, die dies explizit als Erweiterung anbieten.

    Man kann aber auf PODs über einen char* oder unsigned char* zugreifen und per memcpy kopieren. Zum Beispiel so

    wchar_t dings = 23;
      unsigned char* p = reinterpret_cast<unsigned char*>(&dings);
      std::reverse(p,p+sizeof(wchar_t));
    

    Ob dabei aber etwas sinniges bei rumkommt, ist Implementierungssache.

    Ich denke, das Problem kann und sollte man auch ohne diese "Hacks" lösen.



  • EOP schrieb:

    Ich denke nicht, daß reverse funktioniert (hab aber keine Zeit, das jetzt zu testen).

    Wieso sollte das nicht funktionieren?


  • Administrator

    Sebastian Pizer schrieb:

    Ich denke, das Problem kann und sollte man auch ohne diese "Hacks" lösen.

    Die Frage ist halt nur, wie? Wenn du etwas unbedingt als Little-Endian abspeichern musst und nicht weisst, wie die Repräsentation bei diesem Kompiler aussieht oder sogar klar ist, dass er Big-Endian verwendet, dann musst du entsprechende Konvertierungen vornehmen.

    Für fundamentale Typen würde ich dazu wahrscheinlich Bitshifting nehmen, da die Repräsentation der Bits eindeutig definiert ist. Daher irgendetwas in dieser Art machen:

    template<typename FundamentalT>
    void writeLittleEndian(FundamentalT fundamental, char* buffer, std::size_t size)
    {
      assert(buffer && size >= sizeof(fundamental)); // Oder zukünftig gleich static_assert?
    
      for(std::size_t b = 0; b < sizeof(fundamental); ++b, ++buffer)
      {
        char byte = static_cast<char>((fundamental >> (8 * b)) & 0xFF);
        *buffer = byte;
      }
    }
    

    Ich hoffe, ich habe mich mit der Reihenfolge nicht vertan, ich verwechsle Big-Endian und Little-Endian immer wieder, keine Ahnung wieso 🙂
    Aber das Prinzip sollte hier hoffentlich klar werden.

    Ich bin mir jetzt einzig grad nicht mehr sicher, wie es mit der Konvertierung nach char aussieht. Da gab es glaub ich noch irgendein Problem, was aber auf den meisten Kompiler keines ist, dafür nicht ganz Standardkonform ... Müsste ich zuerst nochmals nachschauen gehen.

    Tachyon schrieb:

    EOP schrieb:

    Ich denke nicht, daß reverse funktioniert (hab aber keine Zeit, das jetzt zu testen).

    Wieso sollte das nicht funktionieren?

    Schon mal überlegt, was passiert, wenn man Little-Endian haben möchte und der Kompiler alles als Little-Endian darstellt? Oder womöglich sogar als ein Mixed-Endian?

    Grüssli



  • Dravere schrieb:

    Schon mal überlegt, was passiert, wenn man Little-Endian haben möchte und der Kompiler alles als Little-Endian darstellt? Oder womöglich sogar als ein Mixed-Endian?
    Grüssli

    Der Name der Funktion ist schon Programm. Mehr als der Name sagt, tut die Funktion auch nicht...

    Deine Lösung funktioniert nur für ordinale Typen, obwohl float/double auch fundamental sind.

    Der Krams ist allerdings etwas zusammengestrichen aus Local-Net-Converts, die ich mal für etwas gebastelt habe. Die sehen dann so aus:

    inline bool isBigEndian( )
    {
        Byte            bytes[2]    = {0, 1};
        return (reinterpret_cast<boost::uint16_t&>(*bytes) == 1);
    }
    
    template<typename T>
    inline void toNetConvert(T const & val, Byte * bytes)
    {
        //only fundamental types are convertible 
        //even pure POD structs may cause alignment problems
        //so convert them element by element
        BOOST_STATIC_ASSERT(boost::is_fundamental<T>::value);
        //if the compilation stops here, the converted type
        //is non fundamental
        Byte const * inPtr = reinterpret_cast<Byte const*>(&val);
        if(isBigEndian())
        {
            std::reverse_copy
                ( inPtr
                , inPtr + sizeof(val)
                , bytes );
        }
        else
        {
            std::copy
                ( inPtr
                , inPtr + sizeof(val)
                , bytes );
        }
    }
    
    template<typename T>
    inline void fromNetConvert(Byte const * bytes, T  & val)
    {
        //only fundamental types are convertible 
        //even pure POD structs may cause alignment problems
        //so convert them element by element
        BOOST_STATIC_ASSERT(boost::is_fundamental<T>::value);
        //if the compilation stops here, the converted type
        //is non fundamental
        if(isBigEndian())
        {
            std::reverse_copy
                ( bytes
                , bytes + sizeof(val)
                , reinterpret_cast<Byte*>(&val) );
        }
        else
        {
            std::copy
                ( bytes
                , bytes + sizeof(val)
                , reinterpret_cast<Byte*>(&val) );
        }
    }
    

  • Administrator

    @Tachyon,
    Ok, das ist aber nicht ausschliesslich std::reverse , sondern du machst zuerst eine Unterscheidung. Damit kann man es natürlich machen, wobei deine Lösung allerdings bei Middle(Mixed)-Endian auch nicht mehr funktioniert.

    Auch noch eine Möglichkeit gäbe es, dass man ein Muster anwendet. Das würde wohl auch für float , double und auch für Middle-Endian Zeug funktionieren. Kurz ein wenig Code zur Erklärung:

    // Unter der Annahme assert(sizeof(int) == 4)
    int value = 234242; // Oder sonst ein Wert.
    unsigned int pattern = 0x00010203;
    
    char buffer[4];
    char const* patternIndices = reinterpret_cast<char const*>(&pattern);
    char const* valueBuffer = reinterpret_cast<char const*>(&value);
    
    buffer[0] = valueBuffer[paternIndices[0]];
    buffer[1] = valueBuffer[paternIndices[1]];
    buffer[2] = valueBuffer[paternIndices[2]];
    buffer[3] = valueBuffer[paternIndices[3]];
    

    So ein Muster kann man sich auch je nach Typgrösse generieren lassen. Für double bräuchte man dann womöglich einen uint64_t , was wieder nicht so ganz Standardkonform ist.

    Naja, die Sache ist eher mühsam 🙂

    Grüssli



  • ZOMFG ist das alles dumm! Weil alle auch immer ihren eigenen rotzigen Weg gehen müssen!

    Wie soll man sein Programm denn nun 100% portabel hinbekommen 😡

    Lauter Frickelcode den keiner versteht!


Anmelden zum Antworten