Lexusminds
Lexusminds

Reputation: 415

Zero-padding array in C++

I have a uint8_t array, containing three bytes.

// 1010 1010 1011 1011 1000 0000
uint8_t command[3] = {0xAA, 0xBB, 0x80};

I now want to zero-pad the array with 7 zeroes on the left side, eventually creating the following array (the 4th byte is of no interest to me):

// 0000 0001 0101 0101 0111 0111 0000 0000
uint8_t expected_output[4] = {0x01, 0x55, 0x77, 0x00};

What's the best way to get this zero-padding implemented in C++ ?

Upvotes: 2

Views: 1603

Answers (1)

Evg
Evg

Reputation: 26272

Solution using std::bitset:

std::array<std::uint8_t, 4>
left_pad(const std::array<std::uint8_t, 3>& in) {
    using Bitset = std::bitset<8 * 4>;
    Bitset bitset;

    for (auto b = in.begin(); b != in.end(); ++b, bitset <<= 8)
        bitset |= Bitset{*b};
    bitset >>= 7;

    std::array<std::uint8_t, 4> out;
    for (auto b = out.rbegin(); b != out.rend(); ++b, bitset >>= 8)
        *b = (bitset & Bitset{0xFF}).to_ulong();

    return out;
}

And then:

void foo(std::uint8_t[4]);

void bar() {
    std::array<std::uint8_t, 3> command{0xCA, 0xBB, 0x80};
    std::array<std::uint8_t, 4> out = left_pad(command);
    foo(out.data());
}

If the input length is fixed to 3 bytes and no generalization for larger lengths is needed, std::bitset<8 * 4> can be replaced with std::uint32_t.

Addition after discussion in comments.

This equivalent version

std::array<std::uint8_t, 4>
left_pad2(const std::array<std::uint8_t, 3>& in) {
    using Bitset = std::bitset<8 * 4>;
    Bitset bitset;

    for (auto b = in.begin(); b != in.end(); ++b) {
        bitset <<= 8;
        bitset |= Bitset{*b};
    }
    bitset <<= 1;

    std::array<std::uint8_t, 4> out;
    for (auto b = out.rbegin(); b != out.rend(); ++b, bitset >>= 8)
        *b = (bitset & Bitset{0xFF}).to_ulong();

    return out;
}

gives better assembly code with GCC and -O3 optimization:

left_pad2(std::array<unsigned char, 3ul> const&):
        movzx   eax, BYTE PTR [rdi]
        movzx   edx, BYTE PTR [rdi+1]
        sal     rax, 8
        or      rax, rdx
        movzx   edx, BYTE PTR [rdi+2]
        sal     rax, 8
        or      rax, rdx
        add     rax, rax
        bswap   eax
        ret

versus

left_pad(std::array<unsigned char, 3ul> const&):
        movzx   eax, BYTE PTR [rdi]
        movzx   edx, BYTE PTR [rdi+1]
        sal     rax, 8
        or      rax, rdx
        movzx   edx, BYTE PTR [rdi+2]
        sal     rax, 8
        or      rax, rdx
        sal     rax, 8
        mov     rdx, rax
        shr     rdx, 7
        mov     BYTE PTR [rsp-1], dl
        mov     rdx, rax
        shr     rdx, 15
        mov     BYTE PTR [rsp-2], dl
        mov     rdx, rax
        shr     rax, 31
        shr     rdx, 23
        mov     BYTE PTR [rsp-4], al
        mov     BYTE PTR [rsp-3], dl
        mov     eax, DWORD PTR [rsp-4]
        ret

Upvotes: 3

Related Questions