Remplir une chaîne d’octets

J’ai 65 parameters de différentes longueurs de bits que je dois remplir dans une chaîne d’octets. Les parameters seraient remplis en continu dans une chaîne d’octets. Par exemple, supposons que le premier paramètre ait une longueur de 1 bit, de sorte qu’il serait rempli à la position 0ème bit du 1er octet de la chaîne d’octets. Maintenant, le deuxième paramètre est supposé long de 9 bits. Ainsi, les 7 premiers bits de ce paramètre seront remplis dans le même octet et les 2 prochains bits doivent aller aux positions de 0ème et 1er bits de l’octet suivant. De même, d’autres parameters seraient renseignés dans la chaîne d’octets. J’ai essayé d’écrire une fonction dans laquelle j’ai passé le pointeur sur l’octet actuel, la position du bit et le pointeur source d’où les données seraient copiées. Mais je trouve difficile d’implémenter la logique. J’ai essayé de nombreuses logiques (opération de bit, décalage de bit, rotation, etc.) mais je n’ai pas pu obtenir la bonne. J’apprécierais beaucoup si quelqu’un pouvait me donner une logique / fonction en “C” pour le faire. Vous pouvez également utiliser un prototype de fonction différent.

J’ai écrit un code pour 16 bits comme suit:

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"); } } 

Mais le problème est que supposons que si je passe src = 54647, je reçois le résultat suivant:

temp = 0

bit = 54647, p_buf = bf84da4b p_buf = 1 bitPos = 0 ctr = 0 srcBitVal = 1

bit = 54647, p_buf = bf84da4b p_buf = 3 bitPos = 1 ctr = 1 srcBitVal = 1

bit = 54647, p_buf = bf84da4b p_buf = 7 bitPos = 2 ctr = 2 srcBitVal = 1

bit = 54647, p_buf = bf84da4b p_buf = 7 bitPos = 3 ctr = 3 srcBitVal = 0

bit = 54647, p_buf = bf84da4b p_buf = 23 bitPos = 4 ctr = 4 srcBitVal = 1

bit = 54647, p_buf = bf84da4b p_buf = 55 bitPos = 5 ctr = 5 srcBitVal = 1

bit = 54647, p_buf = bf84da4b p_buf = 119 bitPos = 6 ctr = 6 srcBitVal = 1

bit = 54647, p_buf = bf84da4b p_buf = 119 bitPos = 7 ctr = 7 srcBitVal = 0

Valeur de temp = 0

bit = 54647, p_buf = bf84da4c p_buf = 0 bitPos = 0 ctr = 8 srcBitVal = 1

bit = 54647, p_buf = bf84da4c p_buf = 0 bitPos = 1 ctr = 9 srcBitVal = 0

bit = 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

bit = 54647, p_buf = bf84da4c p_buf = 0 bitPos = 4 ctr = 12 srcBitVal = 1

bit = 54647, p_buf = bf84da4c p_buf = 0 bitPos = 5 ctr = 13 srcBitVal = 0

bit = 54647, p_buf = bf84da4c p_buf = 0 bitPos = 6 ctr = 14 srcBitVal = 1

bit = 54647, p_buf = bf84da4c p_buf = 0 bitPos = 7 ctr = 15 srcBitVal = 1

Valeur de temp = 0

Cependant, le résultat attendu est le suivant: l’octet suivant doit commencer à se remplir avec les valeurs 8 bits et plus de src.

Quelqu’un peut-il m’aider à résoudre ce problème?

Vous avez de la chance. Depuis que j’aime peu tourner, j’ai écrit une implémentation générique d’un BitBuffer juste pour vous. Je ne l’ai pas testé de manière approfondie (par exemple, pas tous les cas difficiles), mais comme vous le verrez, il passe les tests simples que j’ai ajoutés au code ci-dessous.

 #include  #include  #include  #include  #include  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. // Eg "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; } 

Le code n’est pas optimisé pour des performances maximales, mais j’imagine qu’il devrait néanmoins avoir des performances décentes. Toute modification supplémentaire des performances aurait sensiblement augmenté la taille du code et je voulais garder le code plutôt simple. Certains diront peut-être que l’utilisation de >> 3 au lieu de / 8 et de & 0x7 au lieu de % 8 entraînera de meilleures performances. Pourtant, si vous utilisez un compilateur C décent, c’est exactement ce que le compilateur fera de toute façon en interne si vous activez les optimisations et donc J’ai préféré garder le code plus lisible à la place.

Remarque additionnelle
Lorsque vous passez des pointeurs sur des types de données multi-octets, observez l’ordre des octets! Par exemple, le code suivant

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

fonctionne très bien sur une machine big endian (processeur PPC), mais ne donne pas les résultats escomptés sur une petite machine endian (processeur x86, par exemple). Sur une petite machine endian, vous devez d’abord échanger l’ordre des octets. Vous pouvez utiliser htons et htonl à cette fin:

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

Sur les machines big endian, les fonctions / macros htonX ne font généralement rien, car la valeur est déjà htonX dans “ordre des octets du réseau” (big endian), alors que sur une petite machine endian, elles échangent cet ordre.

Passer un pointeur uint8_t fonctionnera toujours sur l’une ou l’autre des machines, ce n’est qu’un seul octet; il n’ya donc pas d’ordre des octets.