K
Vinzen72 schrieb:
Mit ist schon klar, dass das Bit packing unportabel (gefährlich) ist, aber da ich ca. 250 unterschiedliche solcher Nachrichten zu dekodieren habe, habe ich erst mal nach der am schnellsten zu kodierenden Methode gesucht.
Werde aber wohl nicht um di POD-Struct rumkommen...
Ja, 250 verschiedene Nachrichten-Typen sind nervig. Aber ich sehe keine Notwendigkeit für Hacks wie die Bitfeld-Frimelei. Mach das als unsigned char-Array und gut ist. Wenn dir das mit der bit_reader-Klasse nicht gefällt, probier es doch mal damit:
#include <iostream>
#include <cstdint>
#include <cassert>
//----------[ generischer Kram ]----------
// extracts a sequence of bits from an unsigned char array
// at arbitrary bit positions (most significant bits first)
std::uint32_t get_bits_msbf(
const unsigned char data[],
unsigned bit_offset, unsigned nbits)
{
// TODO: Diese Implementierung kann man vielleicht noch vereinfachen.
assert(nbits <= 32);
if (nbits == 0) return 0u;
data += bit_offset / 8;
bit_offset &= 7;
unsigned tlen = 8 - bit_offset;
std::uint32_t temp = *data & (0xFFu >> bit_offset);
if (tlen >= nbits)
return temp >> (tlen-nbits);
while (tlen+8 <= nbits) {
++data;
temp = (temp << 8) | (*data & 0xFFu);
tlen += 8;
}
unsigned missing = nbits - tlen;
if (missing) {
++data;
temp = (temp << missing) | (*data >> (8-missing));
}
return temp;
}
template<unsigned BitOffset, unsigned BitLen, class MessageTag=void>
struct field
{
typedef MessageTag message_tag;
static const unsigned bit_offset = BitOffset;
static const unsigned bit_len = BitLen;
};
template<class MessageTag=void>
struct message_ptr
{
const unsigned char* data;
unsigned length;
explicit message_ptr(const unsigned char *ptr, unsigned len)
: data(ptr), length(len)
{}
template<unsigned BitOffset, unsigned BitLen>
std::uint32_t get(field<BitOffset,BitLen,MessageTag>) const
{
assert(BitOffset+BitLen <= 8*length);
return get_bits_msbf(data,BitOffset,BitLen);
}
};
//----------[ Anwendungsfall ]----------
namespace mp3_stuff {
struct frame_header_tag {};
const field<0,11,frame_header_tag> sync = {};
const field<11,2,frame_header_tag> mpegid = {};
const field<13,2,frame_header_tag> layer = {};
const field<15,1,frame_header_tag> protection_bit = {};
const field<16,4,frame_header_tag> bitrate_index = {};
const field<20,2,frame_header_tag> sampling_rate_index = {};
const field<22,1,frame_header_tag> padding_bit = {};
const field<23,1,frame_header_tag> private_bit = {};
const field<24,2,frame_header_tag> channel_mode = {};
const field<26,2,frame_header_tag> cm_extension = {};
const field<28,1,frame_header_tag> copyrighted_bit = {};
const field<29,1,frame_header_tag> original_bit = {};
const field<30,2,frame_header_tag> emphasis_mode = {};
static const char* const mpegid2str[] = {"MPEG 2.5", "reserved", "MPEG 2", "MPEG 1"};
static const char* const chanmodes[] = {"Stereo", "Joint Stereo", "Dual Channel", "Mono"};
static const long srate[] = {44100,48000,32000};
} // namespace mp3_stuff
void test()
{
using namespace std;
using namespace mp3_stuff;
unsigned char data[] = {0xFF, 0xFB, 0xA0, 0x64};
message_ptr<frame_header_tag> mptr (data,4);
if (mptr.get(sync) == 0x7FF) {
cout << "MPEG : " << mpegid2str[mptr.get(mpegid)] << endl;
cout << "Layer : " << 4-mptr.get(layer) << endl;
cout << "Bitrate index : " << mptr.get(bitrate_index) << endl;
const int srdivisor = 4-mptr.get(mpegid);
cout << "Sampling rate : " << srate[mptr.get(sampling_rate_index)]/srdivisor << endl;
cout << "Channel mode : " << chanmodes[mptr.get(channel_mode)] << endl;
} else {
cout << "Sync bits invalid\n";
}
}
int main()
{
test();
}
Ich denke, damit sollte die Fehleranfälligkeit in der Benutzung nicht so groß sein. Man definiert das Format quasi an einer Stelle und überall sonst kann man nicht mehr viel falsch machen, da man per mptr.get(feld) ein Datum ausliest, siehe Zeilen 92-98. Aufgrund des MessageTag-Parameters sollte das auch genügend typsicher sein.
Was mit gerade auffällt: Es macht ja sogar Sinn, statt get den operator->* zu überladen.