1

オクテット文字列に入力する必要がある異なるビット長の 65 個のパラメーターがあります。パラメータは、オクテット文字列で連続して埋められます。たとえば、最初のパラメータが 1 ビット長であると仮定すると、オクテット文字列の 1 番目のオクテットの 0 番目のビット位置で埋められます。ここで、2 番目のパラメーターは 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

temp の値 = 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 ビット位置 = 2 ctr = 10 srcBitVal = 1

ビット = 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 ビット目以降の値で埋め始める必要があります。

誰かがそれを整理するのを手伝ってくれますか?

4

1 に答える 1

2

あなたは運が良い。私はちょっといじるのが好きなので、あなたのために 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代わりに/ 8and& 0x7の代わりを使用するとパフォーマンスが向上すると主張する人もいるかもしれ% 8ませんが、適切な C コンパイラを使用している場合は、最適化を有効にするとコンパイラが内部的に行うこととまったく同じであるため、代わりにコードを読みやすくすることを好みました。 .

追加メモ
マルチバイト データ型へのポインタを渡すときは、バイト オーダーに注意してください。たとえば、次のコード

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

ビッグ エンディアン マシン (PPC CPU) では正常に動作しますが、リトル エンディアン マシン (x86 CPU など) では期待どおりの結果が得られない場合があります。リトル エンディアンのマシンでは、最初にバイト オーダーを交換する必要があります。この目的htonsで とを使用できます。htonl

 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 ポインターを渡すと、どちらのマシンでも常に機能します。これは 1 バイトのみであるため、バイト オーダーはありません。

于 2013-01-09T11:56:32.053 に答える