128 bit string to array using boost :: spirit :: *
I am currently starting with boost :: spirit :: *. I am trying to parse a 128 bit string into a simple array c with the appropriate size. I created a short test that does the job:
boost::spirit::qi::int_parser< boost::uint8_t, 16, 2, 2 > uint8_hex;
std::string src( "00112233445566778899aabbccddeeff" );
boost::uint8_t dst[ 16 ];
bool r;
for( std::size_t i = 0; i < 16; ++i )
{
r = boost::spirit::qi::parse( src.begin( ) + 2 * i, src.begin( ) + 2 * i + 2, uint8_hex, dst[ i ] );
}
I have a feeling this is not the smartest way to do it :) Any ideas how to define a rule so I can avoid the loop?
Update:
In the meantime, I figured out the following code, which does the job very well:
using namespace boost::spirit;
using namespace boost::phoenix;
qi::int_parser< boost::uint8_t, 16, 2, 2 > uint8_hex;
std::string src( "00112233445566778899aabbccddeeff" );
boost::uint8_t dst[ 16 ];
std::size_t i = 0;
bool r = qi::parse( src.begin( ),
src.end( ),
qi::repeat( 16 )[ uint8_hex[ ref( dst )[ ref( i )++ ] = qi::_1 ] ] );
source to share
Without literally leaving the question, if you really wanted to simply parse the hexadecimal representation of a 128-bit integer, you can do it in a hyphenated uint128_t
manner using the one defined in Boost Multiprecision:
qi::int_parser<uint128_t, 16, 16, 16> uint128_hex;
uint128_t parsed;
bool r = qi::parse(f, l, uint128_hex, parsed);
This will be the fastest way, especially on platforms where 128-bit types are supported in the instruction set.
#include <boost/multiprecision/cpp_int.hpp>
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
int main() {
using boost::multiprecision::uint128_t;
using It = std::string::const_iterator;
qi::int_parser<uint128_t, 16, 16, 16> uint128_hex;
std::string const src("00112233445566778899aabbccddeeff");
auto f(src.begin()), l(src.end());
uint128_t parsed;
bool r = qi::parse(f, l, uint128_hex, parsed);
if (r) std::cout << "Parse succeeded: " << std::hex << std::showbase << parsed << "\n";
else std::cout << "Parse failed at '" << std::string(f,l) << "'\n";
}
source to share
There's a sad combination of factors that lead to this painful edge case
- Boost Fusion can adapt
(boost::)array<>
, but it requires the parser to cast to a set of elements, not a container -
Boost Fusion can adapt these sequences, but they need to be adjusted to allow 16 elements:
#define FUSION_MAX_VECTOR_SIZE 16
-
Even when you do this, the parser directive
qi::repeat(n)[]
expects the attribute to be the container type.
You can get around the whole ugly thing (like Live On Coliru ). This makes it difficult to work on the road.
I would prefer a tiny semantic action here to get the result from qi::repeat(n)[]
:
using data_t = boost::array<uint8_t, 16>;
data_t dst {};
qi::rule<It, data_t(), qi::locals<data_t::iterator> > rule =
qi::eps [ qi::_a = phx::begin(qi::_val) ]
>> qi::repeat(16) [
uint8_hex [ *qi::_a++ = qi::_1 ]
];
It works without too much noise. The idea is to take the start iterator and write each iterator to the next element.
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;
int main() {
using It = std::string::const_iterator;
qi::int_parser<uint8_t, 16, 2, 2> uint8_hex;
std::string const src("00112233445566778899aabbccddeeff");
auto f(src.begin()), l(src.end());
using data_t = boost::array<uint8_t, 16>;
data_t dst {};
qi::rule<It, data_t(), qi::locals<data_t::iterator> > rule =
qi::eps [ qi::_a = phx::begin(qi::_val) ]
>> qi::repeat(16) [
uint8_hex [ *qi::_a++ = qi::_1 ]
];
bool r = qi::parse(f, l, rule, dst);
if (r) {
std::cout << "Parse succeeded\n";
for(unsigned i : dst) std::cout << std::hex << std::showbase << i << " ";
std::cout << "\n";
} else {
std::cout << "Parse failed at '" << std::string(f,l) << "'\n";
}
}
source to share