Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ parse bit stream

I have a 6 byte array consisting of packed binary.

The first 6 bits represent 'int1'

The following 10 bits represent 'int2'

The following 14 bits represent 'int3'

The following 16 bits represent 'int4'

The following 2 bits are spare

I have tried using Boost::Spirit to parse into a struct

struct header_t
{
    union{
        uint16_t firstPart;
        struct{
            uint16_t int1     : 6;
            uint16_t int2     : 10;
        };
    };
    union{
        uint16_t secondPart;
        struct{
            uint16_t int3     : 14;
            uint16_t int4_lo  : 2;
        };
    };
    union{
        uint16_t thirdPart;
        struct{
            uint16_t int4_hi  : 14;
            uint16_t spare    : 2;
    };
  };
};

BOOST_FUSION_ADAPT_STRUCT(
    header_t,
    (uint16_t, firstPart)
    (uint16_t, secondPart)
    (uint16_t, thirdPart))

typedef const char* parse_iter_t;

int main()
{
    using qi::big_word;

    parse_iter_t iter = "\xff\x3e"
                        "\x44\x77"
                        "\x35\x19";

    parse_iter_t iter_end = iter + std::strlen(iter);

    qi::rule< parse_iter_t, header_t() > header_rule;

    header_rule = big_word >> big_word >> big_word;

    header_t result;

    BOOST_TEST(qi::parse( iter, iter_end, header_rule, result ));
}

This sort of works.

int1 = a = 62 (as expected)

int2 = b = 1020 (as expected)

int3 = c = 1143 (as expected)

int4 this is where it sort of falls down. As you can see, the value is split between int4_lo and int4_hi.

How can I improve this, so that int4 in total is recovered during the parsing process?

I am aware I can bitshift and combine to recover int4, however there will be many different message structures that are a lot longer than this example. So I don't want to hand-code shifts etc if I can avoid it.

EDIT - doesn't have to use Boost, this is just an attempt by me to try it.

There will also be dozens of messages like these. However they will be longer, up to 300 bits.

like image 643
user3542787 Avatar asked Nov 17 '25 20:11

user3542787


1 Answers

In this rare case, not using automatic attribute propation.

Instead, I'd make a conversion function and call it from a semantic action.

#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>

namespace qi  = boost::spirit::qi;

struct header_t
{
    header_t(uint16_t i1=0, uint16_t i2=0, uint16_t i3=0, uint16_t i4=0) : int1(i1), int2(i2), int3(i3), int4(i4) { }

    static header_t make(uint16_t w1, uint16_t w2, uint16_t w3)
    {
#define BITS(n) ((1ul << (n)) - 1u)
        return header_t(
                (w1 & BITS(6u)),  // 6
                (w1 >> 6u),       // 10
                (w2 & BITS(14u)), // 14
                                  // 2+14
                (((w3 & BITS(14)) << 2) | (w2 >> 14u) ));
    }

    uint16_t int1; // 6
    uint16_t int2; // 10
    uint16_t int3; // 14
    uint16_t int4; // 2+14
};

typedef const char* parse_iter_t;

int main()
{
    using qi::big_word;

    parse_iter_t iter = "\xff\x3e"
                        "\x44\x77"
                        "\x35\x19";
    //parse_iter_t iter = "\xff\xff"
                        //"\xff\xff"
                        //"\xff\xff";

    parse_iter_t iter_end = iter + std::strlen(iter);

    qi::rule<parse_iter_t, header_t()> header_rule;

    header_rule = (big_word >> big_word >> big_word)
                        [ qi::_val = boost::phoenix::bind(header_t::make, qi::_1, qi::_2, qi::_3) ];

    header_t result;
    bool ok = qi::parse(iter, iter_end, header_rule, result);

    if (ok)
    {
        std::cout << "int1: " << std::dec << result.int1 << " " << std::hex << std::showbase << result.int1 << "\n";
        std::cout << "int2: " << std::dec << result.int2 << " " << std::hex << std::showbase << result.int2 << "\n";
        std::cout << "int3: " << std::dec << result.int3 << " " << std::hex << std::showbase << result.int3 << "\n";
        std::cout << "int4: " << std::dec << result.int4 << " " << std::hex << std::showbase << result.int4 << "\n";
    }

    assert(ok);
}

Prints:

int1: 62 0x3e
int2: 1020 0x3fc
int3: 1143 0x477
int4: 54373 0xd465

See it Live on Coliru

Alternatively you can specialize the attribute transformation traits for header_t.

like image 137
sehe Avatar answered Nov 20 '25 11:11

sehe



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!