2013-01-09 183 views
1

我有65个不同的比特长度的参数,我需要填写一个八位组串。参数将以八位组串连续填充。例如,假设第一个参数是1位长,所以它将被填充到八位组串的第一个八位字节的第0位位置。现在第二个参数是假设9位长。所以这个参数的前7位将被填充到同一个八位字节中,接下来的2个比特应该到达下一个八位字节的第0位和第1位。类似地,其他参数将在八位字符串中被填充。我试图编写一个函数,在该函数中,我将指针传递给当前八位字节,位置以及数据将被复制到的源指针。但是我发现逻辑实现有困难。我尝试了很多逻辑(位操作,位移,旋转等),但无法得到正确的结果。如果有人能给我一个“C”中的逻辑/函数来这样做,我将不胜感激。你也可以使用不同的函数原型。填充八位字符串

我已经写了代码为16位如下:

void set16BitVal(U8** p_buf, U8* bitPos, U16 src) 
{ 
     U16 ctr; 
     U16 bitVal; 
     U16 srcBitVal; 
     U16 tempSrc = src; 
     U8 temp = **p_buf; 
     printf("\n temp = %d\n", temp); 
     for(ctr=0; ctr<16; ctr++) 
     { 
     bitVal = 1; 
     bitVal = bitVal << ctr; 
     srcBitVal = src & bitVal; 

     temp = temp | srcBitVal; 
     **p_buf = temp; 

     if(srcBitVal) 
     srcBitVal = 1; 
     else 
     srcBitVal = 0; 

     printf("\n bit = %d, p_buf = %x \t p_buf=%d bitPos=%d ctr=%d srcBitVal = %d\n",\ 
       tempSrc, *p_buf, **p_buf, *bitPos, ctr, srcBitVal); 
     *bitPos = (*bitPos+1)%8; /*wrap around after bitPos:7 */ 
     if(0 == *bitPos) 
     { 
     (*p_buf)++; /*jump to next octet*/ 
     temp = **p_buf; 
     printf("\n Value of temp = %d\n", temp); 
     } 



     //printf("\n ctr=%d srcBitVal = %d", ctr, srcBitVal); 
     printf("\n"); 
    } 
} 

但问题是,假设如果我通过SRC = 54647,我得到以下输出继电器:

温度= 0

位= 54647,P_BUF = bf84da4b P_BUF = 1 BITPOS = 0 CTR = 0 srcBitVal = 1

位= 54647,P_BUF = bf84da4b P_BUF = 3 BITPOS = 1 CTR = 1 srcBitVal = 1

位= 54647,P_BUF = bf84da4b P_BUF = 7 BITPOS = 2 CTR = 2 srcBitVal = 1

位= 54647,P_BUF = bf84da4b P_BUF = 7 BITPOS = 3 CTR = 3 srcBitVal = 0

位= 54647,P_BUF = bf84da4b P_BUF = 23 BITPOS = 4 CTR = 4 srcBitVal = 1

位= 54647,P_BUF = bf84da4b P_BUF = 55 BITPOS = 5 CTR = 5 srcBitVal = 1

位= 54647,P_BUF = bf84da4b p_buf = 119 bitPos = 6 ctr = 6 srcBitVal = 1

位= 54647,P_BUF = bf84da4b P_BUF = 119 BITPOS = 7 CTR = 7 srcBitVal = 0

值温度= 0

位= 54647,P_BUF = bf84da4c P_BUF = 0 BITPOS = 0 CTR = 8 srcBitVal = 1

位= 54647,P_BUF = bf84da4c P_BUF = 0 BITPOS = 1 CTR = 9 srcBitVal = 0

位= 54647,P_BUF = bf84da4c P_BUF = 0 BITPOS = 2 CTR = 10 srcBitVal = 1

bit = 54647,p_buf = bf84da4c p_buf = 0 bitPos = 3 ctr = 11 srcBitVal = 0

位= 54647,P_BUF = bf84da4c P_BUF = 0 BITPOS = 4 CTR = 12 srcBitVal = 1

位= 54647,P_BUF = bf84da4c P_BUF = 0 BITPOS = 5 CTR = 13 srcBitVal = 0

位= 54647,P_BUF = bf84da4c P_BUF = 0 BITPOS = 6 CTR = 14 srcBitVal = 1

位= 54647,P_BUF = bf84da4c P_BUF = 0 BITPOS = 7 CTR = 15 srcBitVal = 1

的价值temp = 0

但是,预期的输出是:下一个字节应该从src的第8位开始填充。

有人可以帮我解决它吗?

+0

不要发布一个新的问题,编辑[你已经问一个相同(http://stackoverflow.com/questions/14217224/pack-in-an-octet-string)。我正在投票结束,因为这一个包含额外的信息。 – Lundin

回答

1

你很幸运。因为我喜欢玩弄,所以我为你写了一个BitBuffer的通用实现。我没有完全测试它(例如,并非所有讨厌的角落案例),但正如您将看到的,它通过了我添加到下面的代码中的简单测试。

#include <assert.h> 
#include <stdint.h> 
#include <stdlib.h> 
#include <string.h> 
#include <stdbool.h> 

struct BitBuffer { 
    unsigned length;  // No of bits used in buffer 
    unsigned capacity;  // No of bits available in buffer 
    uint8_t buffer[]; 
}; 

struct BitBuffer * newBitBuffer (
    unsigned capacityInBits 
) { 
    int capacityInBytes; 
    struct BitBuffer * result; 

    capacityInBytes = (capacityInBits/8); 
    if (capacityInBits % 8 != 0) { 
     capacityInBytes++; 
    } 

    result = malloc(sizeof(*result) + capacityInBytes); 
    if (result) { 
     result->length = 0; 
     result->capacity = capacityInBits; 
    } 
    return result; 
} 

bool addBitsToBuffer (
    struct BitBuffer * bbuffer, const void * bits, unsigned bitCount 
) { 
    unsigned tmpBuf; 
    unsigned tmpBufLen; 
    unsigned tmpBufMask; 
    uint8_t * nextBufBytePtr; 
    const uint8_t * nextBitsBytePtr; 

    // Verify input parameters are sane 
    if (!bbuffer || !bits) { 
     // Evil! 
     return false; 
    } 
    if (bitCount == 0) { 
     // No data to add? Nothing to do. 
     return true; 
    } 

    // Verify we have enough space available 
    if (bbuffer->length + bitCount > bbuffer->capacity) { 
     // Won't fit! 
     return false; 
    } 

    // Get the first byte we start writing bits to 
    nextBufBytePtr = bbuffer->buffer + (bbuffer->length/8); 

    // Shortcut: 
    // If we happen to be at a byte boundary, 
    // we can simply use memcpy and save us a lot of headache. 
    if (bbuffer->length % 8 == 0) { 
     unsigned byteCount; 

     byteCount = bitCount/8; 
     if (bitCount % 8 != 0) { 
      byteCount++; 
     } 
     memcpy(nextBufBytePtr, bits, byteCount); 
     bbuffer->length += bitCount; 
     return true; 
    } 

    // Let the bit twiddling begin 
    nextBitsBytePtr = bits; 
    tmpBuf = *nextBufBytePtr; 
    tmpBufLen = bbuffer->length % 8; 
    tmpBuf >>= 8 - tmpBufLen; 
    tmpBufMask = (~0u) >> ((sizeof(unsigned) * 8) - tmpBufLen); 
    // tmpBufMask has the first tmpBufLen bits set to 1. 
    // E.g. "tmpBufLen == 3" ==> "tmpBufMask == 0b111 (7)" 
    // or "tmpBufLen == 6" ==> "tmpBufMask = 0b111111 (63)", and so on. 

    // Beyond this point we will neither access bbuffer->length again, nor 
    // can this function fail anymore, so we set the final length already. 
    bbuffer->length += bitCount; 

    // Process input bits in byte chunks as long as possible 
    while (bitCount >= 8) { 
     // Add 8 bits to tmpBuf 
     tmpBuf = (tmpBuf << 8) | *nextBitsBytePtr; 
     // tmpBuf now has "8 + tmpBufLen" bits set 

     // Add the highest 8 bits of tmpBuf to our BitBuffer 
     *nextBufBytePtr = (uint8_t)(tmpBuf >> tmpBufLen); 

     // Cut off the highest 8 bits of tmpBuf 
     tmpBuf &= tmpBufMask; 
     // tmpBuf now has tmpBufLen bits set again 

     // Skip to next input/output byte 
     bitCount -= 8; 
     nextBufBytePtr++; 
     nextBitsBytePtr++; 
    } 

    // Test if we still have bits left. That will be the case 
    // if the input bit count was no integral multiple of 8. 
    if (bitCount != 0) { 
     // Add bitCount bits to tmpBuf 
     tmpBuf = (tmpBuf << bitCount) | (*nextBitsBytePtr >> (8 - bitCount)); 
     tmpBufLen += bitCount; 
    } 

    // tmpBufLen is never 0 here, it must have a value in the range [1, 14]. 
    // We add zero bits to it so that tmpBuf has 16 bits set. 
    tmpBuf <<= (16 - tmpBufLen); 

    // Now we only need to add one or two more bytes from tmpBuf to our 
    // BitBuffer, depending on its length prior to adding the zero bits. 
    *nextBufBytePtr = (uint8_t)(tmpBuf >> 8); 
    if (tmpBufLen > 8) { 
     *(++nextBufBytePtr) = (uint8_t)(tmpBuf & 0xFF); 
    } 
    return true; 
} 



int main() 
{ 
    bool res; 
    uint8_t testData[4]; 
    struct BitBuffer * buf; 

    buf = newBitBuffer(1024); // Can hold up to 1024 bits 
    assert(buf); 

    // Let's add some test data. 

    // Add 1 bit "1" => Buffer "1" 
    testData[0] = 0xFF; 
    res = addBitsToBuffer(buf, testData, 1); 
    assert(res); 

    // Add 6 bits "0101 01" => Buffer "1010 101" 
    testData[0] = 0x54; 
    res = addBitsToBuffer(buf, testData, 6); 
    assert(res); 

    // Add 4 Bits "1100" => Buffer "1010 1011 100" 
    testData[0] = 0xC0; 
    res = addBitsToBuffer(buf, testData, 4); 
    assert(res); 

    // Add 16 Bits "0111 1010 0011 0110" 
    // ==> Buffer "1010 1011 1000 1111 0100 0110 110 
    testData[0] = 0x7A; 
    testData[1] = 0x36; 
    res = addBitsToBuffer(buf, testData, 16); 
    assert(res); 

    // Add 5 Bits "0001 1" 
    // ==> Buffer "1010 1011 1000 1111 0100 0110 1100 0011" 
    testData[0] = 0x18; 
    res = addBitsToBuffer(buf, testData, 5); 
    assert(res); 

    // Buffer should now have exactly a length of 32 bits 
    assert(buf->length == 32); 

    // And it should have the value 0xAB8F46C3 
    testData[0] = 0xAB; 
    testData[1] = 0x8F; 
    testData[2] = 0x46; 
    testData[3] = 0xC3; 
    assert(memcmp(buf->buffer, testData, 4) == 0); 

    free(buf); 
    return 0; 
} 

该代码没有针对最大性能进行优化,但我想它应该有一个不错的表现。任何额外的性能调整都会显着增加代码大小,并且希望保持代码非常简单。有些人可能会认为,使用>> 3代替/ 8& 0x7代替% 8将导致更好的性能,但如果你使用一个体面的C编译器,这正是编译器将在内部做无论如何,如果您启用优化,因此我更愿意保持代码更具可读性。

附注
当你通过指针多字节数据类型,看字节顺序!例如。下面的代码

uint16_t x16 = ...; 
addBitsToBuffer(buf, &x16, ...); 
uint32_t x32 = ...; 
addBitsToBuffer(buf, &x32, ...); 

工作正常大端机(PPC CPU)上,但它可能不会给小端机器上的预期的结果(例如x86处理器)。在一个小端机器上,你必须先交换字节顺序。您可以使用htonshtonl为了这个目的:

uint16_t x16 = ...; 
uint16_t x16be = htons(x16); 
addBitsToBuffer(buf, &x16be, ...); 
uint32_t x32 = ...; 
uint32_t x32be = htonl(x32); 
addBitsToBuffer(buf, &x32be, ...); 

在大端机器,htonX功能/宏通常什么也不做,因为该值已经在“网络字节顺序”(大端),而上一小端机他们会交换字节顺序。

传递一个uint8_t指针总是任一机器上工作,它只是一个字节,因此不存在字节顺序。