Home > Software design >  c portable address encoding
c portable address encoding

Time:07-31

I'm writing a software that, at some point must write internal addresses into a buffer. I wrote the following code which works. But produce warnings when cross-compiling to a target device with an address size smaller than 64 bits.

How can I make this portable without generating errors? I would have expected gcc to ignore the warning due to the condition-always-false around the problematic statement. I get the same behaviour when doing this with a template (where i feed sizeof(void*()) as address_size).

uint8_t decode_address_big_endian(uint8_t* buf, uintptr_t* addr)
    {
        constexpr unsigned int addr_size = sizeof(void*);
        static_assert(addr_size == 1 || addr_size == 2 || addr_size == 4 || addr_size == 8, "Unsupported address size");

        uintptr_t computed_addr = 0;
        unsigned int i = 0;

        if (addr_size >= 8)
        {
            computed_addr |= ((static_cast<uintptr_t>(buf[i  ]) << 56));
            computed_addr |= ((static_cast<uintptr_t>(buf[i  ]) << 48));
            computed_addr |= ((static_cast<uintptr_t>(buf[i  ]) << 40));
            computed_addr |= ((static_cast<uintptr_t>(buf[i  ]) << 32));
        }

        if (addr_size >= 4)
        {
            computed_addr |= ((static_cast<uintptr_t>(buf[i  ]) << 24));
            computed_addr |= ((static_cast<uintptr_t>(buf[i  ]) << 16));
        }

        if (addr_size >= 2)
        {
            computed_addr |= ((static_cast<uintptr_t>(buf[i  ]) << 8));
        }

        if (addr_size >= 1)
        {
            computed_addr |= ((static_cast<uintptr_t>(buf[i  ]) << 0));
        }
        
        *addr = computed_addr;

        return static_cast<uint8_t>(addr_size);
    }

The code works. MSVC is able to optimize that to a single x86_64 instruction. It also works on a AtMega328p, but avr-gcc do throw these warnings.

/home/py/scrutiny-embedded/lib/src/protocol/scrutiny_protocol_tools.cpp:32:72: warning: left shift count >= width of type [-Wshift-count-overflow]
                 computed_addr |= ((static_cast<uintptr_t>(buf[i  ]) << 56));
                                                                        ^
/home/py/scrutiny-embedded/lib/src/protocol/scrutiny_protocol_tools.cpp:33:72: warning: left shift count >= width of type [-Wshift-count-overflow]
                 computed_addr |= ((static_cast<uintptr_t>(buf[i  ]) << 48));
                                                                        ^
/home/py/scrutiny-embedded/lib/src/protocol/scrutiny_protocol_tools.cpp:34:72: warning: left shift count >= width of type [-Wshift-count-overflow]
                 computed_addr |= ((static_cast<uintptr_t>(buf[i  ]) << 40));
                                                                        ^
/home/py/scrutiny-embedded/lib/src/protocol/scrutiny_protocol_tools.cpp:35:72: warning: left shift count >= width of type [-Wshift-count-overflow]
                 computed_addr |= ((static_cast<uintptr_t>(buf[i  ]) << 32));
                                                                        ^
/home/py/scrutiny-embedded/lib/src/protocol/scrutiny_protocol_tools.cpp:40:72: warning: left shift count >= width of type [-Wshift-count-overflow]
                 computed_addr |= ((static_cast<uintptr_t>(buf[i  ]) << 24));
                                                                        ^
/home/py/scrutiny-embedded/lib/src/protocol/scrutiny_protocol_tools.cpp:41:72: warning: left shift count >= width of type [-Wshift-count-overflow]
                 computed_addr |= ((static_cast<uintptr_t>(buf[i  ]) << 16));
                                                                        ^

In this case, I can make a static_assert on address_size to prove that it is indeed equal to 2. But still get the warnings.

CodePudding user response:

Try

if constexpr (addr_size >= 8)

If C 17 is not available to you, you can suppress the warning

if (addr_size >= 8) {
   computed_addr |= ((static_cast<uintptr_t>(buf[i  ]) << ((addr_size >= 8) * 56)));
   computed_addr |= ((static_cast<uintptr_t>(buf[i  ]) << ((addr_size >= 8) * 48)));
   computed_addr |= ((static_cast<uintptr_t>(buf[i  ]) << ((addr_size >= 8) * 40)));
   computed_addr |= ((static_cast<uintptr_t>(buf[i  ]) << ((addr_size >= 8) * 32)));
}

If addr_size >= 8 is true, it evaluates to 1 in the multiplication arguments. Otherwise, it would evaluate to 0, but the branch is not executed.

Shorter with @RaymondChen's hint.

if (addr_size >= 8) {
   computed_addr |= ((static_cast<uintptr_t>(buf[i  ]) << (56 % (addr_size * 8));
   computed_addr |= ((static_cast<uintptr_t>(buf[i  ]) << (48 % (addr_size * 8));
   computed_addr |= ((static_cast<uintptr_t>(buf[i  ]) << (40 % (addr_size * 8));
   computed_addr |= ((static_cast<uintptr_t>(buf[i  ]) << (32 % (addr_size * 8));
}
// Will be shorter with constexpr unsigned int addr_size = sizeof(void*) * 8;

Alternative code

if (addr_size >= 8) {
   uint64_t tmp;
   tmp = static_cast<uintptr_t>(buf[i  ]) << 28;
   computed_addr |= tmp << 28;
   tmp = static_cast<uintptr_t>(buf[i  ]) << 24;
   computed_addr |= tmp << 24;
   tmp = static_cast<uintptr_t>(buf[i  ]) << 20;
   computed_addr |= tmp << 20;
   tmp = static_cast<uintptr_t>(buf[i  ]) << 16;
   computed_addr |= tmp << 16;
}

CodePudding user response:

I would forgo bitpacking and just do an in-place conversion of bytes on little-endian devices.

To deserialize an address from big-endian, you only have to do this:

size_t decode_address_big_endian(uint8_t* buf, uintptr_t* addr)
{
    const size_t address_size = sizeof(uintptr_t);
    uint32_t test = 0x01020304;
    bool isLittleEndian = (htonl(test) != test); // isLittleEndian = (1 != *(uint8_t*)&test));


    if (isLittleEndian)
    {
        uint8_t tmp[sizeof(uintptr_t)];
        memcpy(tmp, buf, address_size);
        for (size_t i = 0; i < address_size/2; i  )
        {
            std::swap(tmp[i], tmp[address_size-1-i]);
        }
        memcpy(addr, tmp, address_size);
    }
    else
    {
        memcpy(addr, buf, address_size);
    }
    return address_size;
}
  • Related