Deserialize en byte array til en struct

stemmer
5

Jeg får en overføring over nettverk som er en rekke chars / bytes. Den inneholder en topptekst og noen data. Jeg ønsker å kartlegge overskriften på en struct. Her er et eksempel:

#pragma pack(1)

struct Header
{
    unsigned short bodyLength;
    int msgID;
    unsigned short someOtherValue;
    unsigned short protocolVersion;
};

int main()
{
    boost::array<char, 128> msgBuffer;
    Header header;

    for(int x = 0; x < sizeof(Header); x++)
        msgBuffer[x] = 0x01; // assign some values

    memcpy(&header, msgBuffer.data(), sizeof(Header));

    system(PAUSE);    

    return 0;
}

Vil dette alltid fungerer antar strukturen aldri inneholder noen variabel lengde felt? Er det en plattformuavhengig / idiomatiske måten å gjøre dette?

Merk:

Jeg har sett ganske mange bibliotekene på internett som lar deg serial / Deserialize, men jeg får inntrykk av at de bare kan deserialize noe om den har ben tidligere serialisert med samme bibliotek. Vel, jeg har ingen kontroll over formatet for overføring. Jeg er definitivt kommer til å få en byte / røye matrise der alle verdiene bare følger på hverandre.

Publisert på 06/02/2009 klokken 11:15
kilden bruker
På andre språk...                            


6 svar

stemmer
5

Bare ren kopiering er svært sannsynlig å bryte, i hvert fall hvis dataene kan komme fra en annen arkitektur (eller bare kompilator) enn hva du er på. Dette er på grunn av:

Det andre leddet er GCC-spesifikke, men dette gjelder alle kompilatorer.

Jeg anbefaler å lese feltene byte-for-byte, og montering større felt (ints, etc) fra disse bytes. Dette gir deg kontroll over endianness og polstring.

Svarte 06/02/2009 kl. 11:35
kilden bruker

stemmer
5

Noen prosessorer krever at visse typer er riktig justert. De vil ikke godta den angitte pakking og generere en hardware felle.

Og selv på vanlig x86 pakket strukturer kan føre til at koden til å kjøre saktere.

Også må du ta vare når du arbeider med ulike endianness plattformer.

Forresten, hvis du vil ha en enkel og plattformuavhengig kommunikasjonsmekanisme med bindinger til mange programmeringsspråk, så ta en titt på Yami .

Svarte 06/02/2009 kl. 11:30
kilden bruker

stemmer
2

Den #pragma pack(1)Direktivet skal fungere på de fleste kompilatorer, men du kan sjekke ved å arbeide ut hvor stor datastrukturen skal være (10 i ditt tilfelle hvis mine matematikk er korrekt) og bruke printf("%d", sizeof(Header));for å sjekke at pakningen blir gjort.

Som andre har sagt du fortsatt trenger å være skeptisk til endianness hvis du kommer mellom arkitekturer.

Svarte 06/02/2009 kl. 11:35
kilden bruker

stemmer
1

Fortell meg om jeg tar feil, men AFAIK, gjør det på den måten vil garantere deg at dataene er riktig - forutsatt at de typene har samme størrelse på ulike plattformer :

#include <array>
#include <algorithm>

//#pragma pack(1) // not needed

struct Header
{
    unsigned short bodyLength;
    int msgID;
    unsigned short someOtherValue;
    unsigned short protocolVersion;
    float testFloat;

    Header() : bodyLength(42), msgID(34), someOtherValue(66), protocolVersion(69), testFloat( 3.14f ) {}
};

int main()
{
    std::tr1::array<char, 128> msgBuffer;
    Header header;

    const char* rawData = reinterpret_cast< const char* >( &header );

    std::copy( rawData, rawData + sizeof(Header), msgBuffer.data()); // assuming msgBuffer is always big enough

    system("PAUSE");    

    return 0;
}

Hvis typene er forskjellige på de målrettede plateforms, må du bruker aliaser (typedef) for hver type for å være sikker på at størrelsen på hver brukte typen er den samme.

Svarte 06/02/2009 kl. 18:07
kilden bruker

stemmer
1

Jeg er sterkt uenig med ideen om å lese byte av byte. Hvis du tar vare på strukturen pakking i struct erklæringen, kan du kopiere inn i struct uten problem. For endiannes problemet igjen lese byte av byte løser problemet, men gir deg ikke en generisk løsning. Denne metoden er veldig halt. Jeg har gjort noe som dette før for en tilsvarende jobb, og det fungerte allright uten svikt.

Tenk på dette. Jeg har en struktur, jeg har også en tilsvarende definisjon av denne strukturen. Du kan konstruere dette for hånd, men jeg har hadde skrevet en parser for dette, og brukte den til andre ting også.

For eksempel, definisjonen av strukturen du ga ovenfor er "sis s". (S = kort, i = int) Da gir jeg struct adresse, denne definisjonen og struktur pakking alternativet på denne struct til en spesiell funksjon som omhandler endiannes ting og voila det er gjort.

SwitchEndianToBig (& mål "sis s", 4); // 4 = struktur pakking alternativ

Svarte 06/02/2009 kl. 12:27
kilden bruker

stemmer
0

Jeg vet hvem jeg kommuniserer med, så jeg har egentlig ikke trenger å bekymre deg endianness. Men jeg liker å holde seg borte fra kompilatoren spesifikke kommandoer uansett.

Så hva med denne:

const int kHeaderSizeInBytes = 6;

struct Header
{
    unsigned short bodyLength;
    unsigned short msgID;
    unsigned short protocolVersion; 

    unsigned short convertUnsignedShort(char inputArray[sizeof(unsigned short)])
        {return (((unsigned char) (inputArray[0])) << 8) + (unsigned char)(inputArray[1]);}

    void operator<<(char inputArray[kHeaderSizeInBytes])
    {
        bodyLength = convertUnsignedShort(inputArray);
        msgID = convertUnsignedShort(inputArray + sizeof(bodyLength));
        protocolVersion = convertUnsignedShort(inputArray + sizeof(bodyLength) + sizeof(msgID));
    }
};

int main()
{
    boost::array<char, 128> msgBuffer;
    Header header;

    for(int x = 0; x < kHeaderSizeInBytes; x++)
        msgBuffer[x] = x;

    header << msgBuffer.data();

    system("PAUSE");    

    return 0;
}

Blir kvitt den pragma, men det er ikke så generell som jeg ønsker. Hver gang du legger til et felt i overskriften du må endre << funksjon. Kan du iterere over struct felt eller annen måte, få den type av feltet og ringe den aktuelle funksjonen?

Svarte 06/02/2009 kl. 12:16
kilden bruker

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more