Search code examples
c++structc++17unions

Struct union packing, unpacking and storing


I recently started to play with C++'s advanced data types, pretty new to this data type. And I have not mastered bit shifting either, so please take this question with grain of salt.

I want to use unions in my new project to store variables in a single MySQL field. e.g. rather than having multiple fields for each parts I want to use one field with preferably varchar field (depending on the bit size length) and store this variable in only one field.

First of all, I am compiling my project as x86, so endianness is not an issue based on my researches about unions.

Let's say I have this current data structure;

// Current Data Structure
enum
{
    PLAYER_PART1,
    PLAYER_PART2,
    PLAYER_PART3,
    PLAYER_PART4,
    PLAYER_PART5,
    PLAYER_PART_MAX_NUM,
};

struct SPlayerData
{
    //...
    uint16_t part[PLAYER_PART_MAX_NUM];
} TPlayerData;

void StoreData()
{
    //... Update MySQL table with m_PlayerData.part[N];
    //.. Do this seperately for each MySQL field e.g. "PLAYER_PART2" = m_PlayerData.part[PLAYER_PART2]
    //.. So in total we have five player part fields in MySQL table.
}

void UpdateData()
{
    // To update the parts
    // I get the index of the part and set it.
    // m_PlayerData.part[PLAYER_PART2] = N;
}

And... this is my new version for storing parts;

typedef union
{
    struct s_piece
    {
        uint16_t part1; // PLAYER_PART1
        uint16_t part2; // PLAYER_PART2
    } piece;

    uint32_t package; // 32 Bits
} TPartWear;

struct SPlayerData
{
    //...
    TPartWear part;
} TPlayerData;

void StoreData()
{
    // Now we have only one 32 bit integer
    // Set update: m_PlayerData.part.package; AWESOME!
    // And we dont need these multiple repetitive PLAYER_PARTX fields anymore.
}

void UpdateData()
{
    // To update the parts
    // I just set m_PlayerData.part.piece.part1 = N;
    // Now it is simpler and more readable.
}

Now that's a beauty, I can store parts in only one MySQL field and save some more space and have more readability overall. Very nice.

THE QUESTION

As you can see on the new version I am having 2x16 bits of unsigned shorts, and package value type is 32 bits. Now I can go up to 64 bits if I use 4x unsigned shorts (4x16). That's cool.

The problem is... How am I supposed to have let's say 5x unsigned shorts which is 5x16=80 bits. How am I supposed to handle this > 64 bits numbers in an union struct? Am I going to have a char array or some sort? And what would be more efficient and safest way to do this?

Also, I want to access to package variable as easy as possible, I do not want to manually shift the bytes...

Thank you!


Solution

  • Alright, after a little bit more research I decided to go with storing as BLOB approach.

    Here is a working example of what I did:

    // Data Structure
    enum EPlayerParts
    {
        PLAYER_PART_HEAD,
        PLAYER_PART_ARMOR,
        PLAYER_PART_WEAPON,
        // ...
        PLAYER_PART_MAX_NUM,
    };
    
    typedef struct SPlayerData
    {
        //...
        uint16_t parts[PLAYER_PART_MAX_NUM]; // or WORD or unsigned short
    };
    
    // Functions
    void CreatePlayerPrototype(/*args...*/)
    {
        // Prepping for binary conversion
        static char s_binary[256 + 1];
        static char s_queryStr[256 + 1];
    
        // Escape
        mysql_real_escape_string(SQLHandle, s_binary, m_playerData->parts, sizeof(m_playerData->parts));
    
        // Binary conversion
        _snprintf(s_queryStr, sizeof(s_queryStr), "... '%s' ...", /*...*/ s_binary /*...*/);
    
        // Insert ...
    }
    
    void SetPlayerPrototype(SQLMsg* msg)
    {
        // ... SQL res get message and loop
        size_t int column = 0;
        
        // Copy over BLOB data
        memcpy(m_playerData.parts, row[column++], sizeof(m_playerData.parts));
    }
    
    void SetPlayerPart(uint8_t partPos, uint16_t partVal)
    {
        assert(partPos < PLAYER_PART_MAX_NUM);
        m_playerData.parts[partPos] = partVal;
    }
    
    uint16_t GetPlayerPart(uint8_t partPos)
    {
        assert(partPos < PLAYER_PART_MAX_NUM);
        return m_playerData.parts[partPos];
    }
    

    I store values as BLOB in MySQL table in only one row, after I fetch the target BLOB value, I execute memcpy to target structure. And rest of it is just type punning into the structure.

    When I am going to store the structure with snprintf I transform structure into a BLOB array and insert / update in the database table field.

    So far, everything's working fine and passed all of my testings. If you have anymore recommendation regarding to make it better or faster or any concerns, please do comment.

    Edit: Depending on the size of my structure I went for tinyblob type in MySQL field. It's capacity is 255 bytes.