Converting a struct of integers into a bitmask - c++

Is it possible (if so, how) to convert a struct of integers into a bitmask. One bit for each integer (0 if the int is 0, otherwise 1). For example
struct Int_List_t
{
uint64_t int1;
uint64_t int2;
uint64_t int3;
uint64_t int4;
} int_list={10,0,5,0};
char int_mask = somefunction(int_list);
//Would contain 1010
||||
|||+-- int4 is 0
||+--- int3 is not 0
|+---- int2 is 0
+----- int1 is not 0

You could just do it explicitly:
char mask(const Int_List_t& vals)
{
return (vals.int1 ? 0x8 : 0x0) |
(vals.int2 ? 0x4 : 0x0) |
(vals.int3 ? 0x2 : 0x0) |
(vals.int4 ? 0x1 : 0x0);
}
If you passed in an array instead of a struct, you could write a loop:
template <size_t N>
uint64_t mask(uint64_t (&vals)[N])
{
uint64_t result = 0;
uint64_t mask = 1 << (N - 1);
for (size_t i = 0; i < N; ++i, mask >>= 1) {
result |= (vals[i] ? mask : 0);
}
return result;
}
If you're open to completely bypassing any type safety whatsoever, you could even implement the above by just reinterpreting your object to be a pointer, although I wouldn't necessarily recommend it:
template <typename T>
uint64_t mask(const T& obj)
{
const uint64_t* p = reinterpret_cast<const uint64_t*>(&obj);
const uint64_t N = sizeof(T)/8;
uint64_t result = 0;
uint64_t mask = 1 << (N - 1);
for (size_t i = 0; i < N; ++i, ++p, mask >>= 1) {
result |= (*p ? mask : 0);
}
return result;
}

Related

Convert array of bits to an array of bytes

I want to convert an array of bits (bool* bitArray) where the values are 1s and 0s into an array of bytes (unsigned char* byteArray) where the values at each index would be one byte.
For ex, index 0~7 in bitArray would go into byteArray[1].
How would I go about doing this? Assuming that I already have an array of bits (but the amount would be subject to change based on the incoming data).
I am not worried about having it divisible by 8 because I will just add padding at the end of the bitArray to make it divisible by 8.
Just just use bit shifts or a lookup array and and combine numbers with 1 bit set each with bitwise or for 8 bits at a time:
int main() {
bool input[] = {
false, false, false, true, true, true, false, false, false,
false, false, false, true, true, true, false, false, false,
false, false, false, true, true, true, false, false, false,
false, false, false, true, true, true, false, false, false,
};
constexpr auto len = sizeof(input) / sizeof(*input);
constexpr size_t outLen = ((len % 8 == 0) ? 0 : 1) + len / 8;
uint8_t out[outLen];
bool* inPos = input;
uint8_t* outPos = out;
size_t remaining = len;
// output bytes where there are all 8 bits available
for (; remaining >= 8; remaining -= 8, ++outPos)
{
uint8_t value = 0;
for (size_t i = 0; i != 8; ++i, ++inPos)
{
if (*inPos)
{
value |= (1 << (7 - i));
}
}
*outPos = value;
}
if (remaining != 0)
{
// output byte that requires padding
uint8_t value = 0;
for (size_t i = 0; i != remaining; ++i, ++inPos)
{
if (*inPos)
{
value |= (1 << (7 - i));
}
}
*outPos = value;
}
for (auto v : out)
{
std::cout << static_cast<int>(v) << '\n';
}
return 0;
}
The rhs of the |= operator could also be replaced with a lookup in the following array, if you consider this simpler to understand:
constexpr uint8_t Bits[8]
{
0b1000'0000,
0b0100'0000,
0b0010'0000,
0b0001'0000,
0b0000'1000,
0b0000'0100,
0b0000'0010,
0b0000'0001,
};
...
value |= Bits[i];
...
You should be using std::bitset for an array of bools, or std::vector<bool> if it's dynamically sized. And std::array for the array or again std::vector for dynamic size. I've only done static size below and conversion to and from.
Converting involves a lot of bit shifts and loops for something that should be a memcpy (on little endian or unsigned char types). The compiler output for -O2 is bad. -O3 removes the loop and to_array2 gets interesting. gcc nearly manages to optimize it, clang actually gets it down to movzx eax, word ptr [rdi]: https://godbolt.org/z/4chb8o81e
#include <array>
#include <bitset>
#include <climits>
template <typename T, std::size_t len>
constexpr std::bitset<sizeof(T) * CHAR_BIT * len> from_array(const std::array<T, len> &arr) {
std::bitset<sizeof(T) * CHAR_BIT * len> res;
std::size_t pos = 0;
for (auto x : arr) {
for(std::size_t i = 0; i < sizeof(T) * CHAR_BIT; ++i) {
res[pos++] = x & 1;
x >>= 1;
}
}
return res;
}
template <typename T, std::size_t len>
constexpr std::array<T, (len + sizeof(T) * CHAR_BIT - 1) / (sizeof(T) * CHAR_BIT)> to_array(const std::bitset<len> &bit) {
std::array<T, (len + sizeof(T) * CHAR_BIT - 1) / (sizeof(T) * CHAR_BIT)> res;
T mask = 1;
T t = 0;
std::size_t pos = 0;
for (std::size_t i = 0; i < len; ++i) {
if (bit[i]) t |= mask;
mask <<= 1;
if (mask == 0) {
mask = 1;
res[pos++] = t;
t = 0;
}
}
if constexpr (len % (sizeof(T) * CHAR_BIT) != 0) {
res[pos] = t;
}
return res;
}
std::bitset<16> from_array2(const std::array<unsigned char, 2> &arr) {
return from_array(arr);
}
std::array<unsigned short, 1> to_array2(const std::bitset<16> &bits) {
return to_array<unsigned short>(bits);
}
#include <iostream>
int main() {
std::array<unsigned char, 2> arr{0, 255};
std::bitset bits = from_array(arr);
std::cout << bits << std::endl;
std::bitset<16> bits2{0x1234};
std::array<unsigned short, 1> arr2 = to_array<unsigned short>(bits2);
std::cout << std::hex << arr2[0] << std::endl;
}

How to calculate data buffer to zero checksum value with as little data as it's possible [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 years ago.
Improve this question
Given this checksum calculation function in C++
int calcCrcPartial(unsigned short* lpdwBlockData, unsigned short dwBlockSizeInBytes, int iInitialCrc)
{
unsigned short dwBlocksSizeInWords;
int bIsSigned;
signed int j;
signed int i;
unsigned short dwDataItem;
bool bIsNegative;
// number of WORD blocks
dwBlocksSizeInWords = dwBlockSizeInBytes >> 1;
for (i = 0; ; ++i)
{
if (dwBlocksSizeInWords <= i)
{
break;
}
dwDataItem = lpdwBlockData[i];
if (dwDataItem != 0)
{
bInvalidCrc = false;
}
for (j = 0; j <= 15; ++j)
{
bIsSigned = (dwDataItem & 0x8000u) != 0;
dwDataItem <<= 1;
bIsNegative = iInitialCrc < 0;
iInitialCrc <<= 1;
iInitialCrc += bIsSigned;
if (bIsNegative)
{
iInitialCrc ^= 0x400007u;
}
}
}
return iInitialCrc;
}
Task:
How to write a function to generate a valid block of data lpdwBlockData (512 bytes) that will make function calcCrcPartial() to return 0 for any given iInitialCrc (calculated from previous calls to this function)?
The CRC is not stored in the block.
The generated block of data (512 bytes) can contain any data.
I have tried to fill the buffer with random data in hope it will hit the 0 checksum value after CRC calculation, but I guess it's not the way...
How to reverse this algorithm and generate valid data to make the calcCrcPartial() on the generated buffer data and provided iInitialCrc to return value 0?
This is not a normal CRC. The initial CRC is cycled left 16 times, then the first short is XOR'ed to the lower 16 bits of the CRC, which is then cycled again 16 times, and the next short is XOR'ed to the lower 16 bits of the CRC. If the data is just 2 shorts, it's the same as cycling the initial CRC 32 times, then XOR'ing the 2 shorts to the cycled CRC. To get the CRC==0, just set the 2 shorts to the initial CRC cycled 32 times. Example code below.
How to calculate data buffer to zero checksum value with as little data as possible
Just need 2 shorts to do this. Set the 2 shorts = 0, calculate CRC, then set the 2 shorts to the calculated CRC. This will result in a CRC of 0 for any initial CRC.
I switched to a table driven version of the checksum function, but the code below also includes a "cleaned up" version of the questions example CRC function.
This code compares the CRC outputs from the questions code, an alternate version, and a table driven version:
#include <iostream>
#include <iomanip>
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
uint32_t crctbl[65536];
void gentbl()
{
uint32_t crc;
int i, j;
for(j = 0; j < 0x10000; j++){
crc = j<<16;
for(i = 0; i < 16; i++)
// assumes twos complement
crc = (crc<<1)^((0-(crc>>31))&0x400007u);
crctbl[j] = crc;
}
}
int calcCrcPartial(unsigned short* lpdwBlockData, unsigned short dwBlockSizeInBytes, int iInitialCrc)
{
unsigned short dwBlocksSizeInWords;
int bIsSigned;
signed int j;
signed int i;
unsigned short dwDataItem;
bool bIsNegative;
// number of WORD blocks
dwBlocksSizeInWords = dwBlockSizeInBytes >> 1;
for (i = 0; ; ++i)
{
if (dwBlocksSizeInWords <= i)
{
break;
}
dwDataItem = lpdwBlockData[i];
// bInvalidCrc not delcared and not used
// if (dwDataItem != 0)
// {
// bInvalidCrc = false;
// }
for (j = 0; j <= 15; ++j)
{
bIsSigned = (dwDataItem & 0x8000u) != 0;
dwDataItem <<= 1;
bIsNegative = iInitialCrc < 0;
iInitialCrc <<= 1;
iInitialCrc += bIsSigned;
if (bIsNegative)
{
iInitialCrc ^= 0x400007u;
}
}
}
return iInitialCrc;
}
// alternate version of calcCrcPartial
uint32_t calcCrcPartiala(uint16_t* lpwBlockData, uint16_t wBlockSizeInBytes, uint32_t iInitialCrc)
{
int sz = wBlockSizeInBytes >> 1;
int i;
while(sz--){
for(i = 0; i < 16; i++)
// assumes twos complement
iInitialCrc = (iInitialCrc<<1)^((0-(iInitialCrc>>31))&0x400007u);
iInitialCrc ^= *lpwBlockData++;
}
return iInitialCrc;
}
// table version of calcCrcPartial
uint32_t calcCrcPartialt(uint16_t* lpwBlockData, uint16_t wBlockSizeInBytes, uint32_t iInitialCrc)
{
int sz = wBlockSizeInBytes >> 1;
while(sz--)
iInitialCrc = (iInitialCrc<<16)^crctbl[iInitialCrc>>16]^*lpwBlockData++;
return iInitialCrc;
}
int main()
{
uint16_t data[] = {0x0000, 0x0000};
uint32_t iCrc, oCrc, oCra, oCrt;
gentbl();
iCrc = 0x00000000u;
do{
oCrc = calcCrcPartial (data, sizeof(data), iCrc);
oCra = calcCrcPartiala(data, sizeof(data), iCrc);
oCrt = calcCrcPartiala(data, sizeof(data), iCrc);
if(oCrc != oCra || oCrc != oCrt){
std::cout << "mismatch" << std::endl;
break;}
if ((iCrc & 0x0ffffffu) == 0)
std::cout << std::hex << iCrc << std::endl;
}while(++iCrc != 0x10000000u);
return 0;
}
This code tests all 4 billion possible initial CRCs.
#include <iostream>
#include <iomanip>
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
uint32_t crctbl[65536];
void gentbl()
{
uint32_t crc;
int i, j;
for(j = 0; j < 0x10000; j++){
crc = j<<16;
for(i = 0; i < 16; i++)
// assumes twos complement
crc = (crc<<1)^((0-(crc>>31))&0x400007u);
crctbl[j] = crc;
}
}
uint32_t calcCrcPartial(uint16_t* lpwBlockData, uint16_t wBlockSizeInBytes, uint32_t iInitialCrc)
{
int sz = wBlockSizeInBytes >> 1;
while(sz--)
iInitialCrc = (iInitialCrc<<16)^crctbl[iInitialCrc>>16]^*lpwBlockData++;
return iInitialCrc;
}
// alternate version of questions code
uint32_t calcCrcPartialx(uint16_t* lpwBlockData, uint16_t wBlockSizeInBytes, uint32_t iInitialCrc)
{
int sz = wBlockSizeInBytes >> 1;
int i;
while(sz--){
for(i = 0; i < 16; i++)
// assumes twos complement
iInitialCrc = (iInitialCrc<<1)^((0-(iInitialCrc>>31))&0x400007u);
iInitialCrc ^= *lpwBlockData++;
}
return iInitialCrc;
}
int main()
{
uint16_t data[] = {0x0000, 0x0000};
uint32_t iCrc, oCrc;
gentbl();
iCrc = 0x00000000u;
do{
// oCrc = iCrc cycled 32 times
data[0] = 0x0000;
data[1] = 0x0000;
oCrc = calcCrcPartial(data, 4, iCrc);
// store oCrc and verify new crc == 0
data[0] = (oCrc>>16);
data[1] = (oCrc>> 0);
oCrc = calcCrcPartial(data, 4, iCrc);
if (oCrc != 0) {
std::cout << "error" << std::endl;
break;
}
if ((iCrc & 0xfffffffu) == 0)
std::cout << std::hex << iCrc << std::endl;
}while(++iCrc != 0x00000000u);
return 0;
}
How to calculate data buffer to zero checksum value with as little data as possible
If this means the minimum number of error bits, then in a buffer of 34 shorts, all zero, and initial CRC = 0, 6 bits in specific locations (based on poly and initial CRC) need to be toggled to also produce a CRC = 0:
0x8000, 0x0000, 0x0000, 0x0000, 0x0000,0x0000,0x0000,0x0000,
0x0000, 0x0000, 0x8000, 0x0000, 0x0000,0x0000,0x0000,0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000,0x0000,0x0000,0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000,0x0000,0x0020,0x8003,
0x0000, 0x0000
or simple still use the CRC polynomial which only has 5 bits = 1, only 5 words needed:
0x0100, 0x4000, 0x0700, 0x0000, 0x0000
This is how this version of CRC could be used for 512 bytes of data and 4 bytes of CRC:
#include <stdlib.h> // for rand()
// # of shorts in data, not including CRC
#define COUNT 256
int main()
{
uint16_t data[COUNT+2];
uint32_t iCrc, oCrc;
int i;
gentbl();
// fill data with psuedo random values
for(i = 0; i < COUNT; i++)
data[i] = ((rand()>>4)&0xff)|((rand()<<4)&0xff00);
iCrc = 0x00000000u;
do{
// generate crc
data[COUNT+0] = 0x0000u;
data[COUNT+1] = 0x0000u;
oCrc = calcCrcPartial(data, sizeof(data), iCrc);
// append crc to data
data[COUNT+0] = (oCrc>>16);
data[COUNT+1] = (oCrc>> 0);
// check crc
oCrc = calcCrcPartial(data, sizeof(data), iCrc);
if (oCrc != 0) {
std::cout << "error" << std::endl;
break;
}
if ((iCrc & 0xfffffu) == 0)
std::cout << std::hex << iCrc << std::endl;
}while(++iCrc != 0x01000000u);
return 0;
}

Deserialization of uint8 array to int64 fails but should work

Im going to send a int64 over tcp and need to serialize&deserialize it.
First i cast it to a uin64.
I byteshift it into an uint8 array.
Then i byteshift the array into a uint64
And finally cast it back to a int.
But it returns a different value than i put in...
I have checked the hex values, but they are supposed to be correct...
Code:
#include <math.h>
#include <string.h>
#include <iostream>
#include <iomanip>
//SER & D-SER int64
std::array<uint8_t, 8> int64ToBytes(int64_t val)
{
uint64_t v = (uint64_t)val;
std::array<uint8_t, 8> bytes;
bytes[0] = (v&0xFF00000000000000)>>56;
bytes[1] = (v&0x00FF000000000000)>>48;
bytes[2] = (v&0x0000FF0000000000)>>40;
bytes[3] = (v&0x000000FF00000000)>>32;
bytes[4] = (v&0x00000000FF000000)>>24;
bytes[5] = (v&0x0000000000FF0000)>>16;
bytes[6] = (v&0x000000000000FF00)>>8;
bytes[7] = (v&0x00000000000000FF);
return bytes;
}
int64_t bytesToInt64(uint8_t bytes[8])
{
uint64_t v = 0;
v |= bytes[0]; v <<= 8;
v |= bytes[1]; v <<= 8;
v |= bytes[3]; v <<= 8;
v |= bytes[4]; v <<= 8;
v |= bytes[5]; v <<= 8;
v |= bytes[6]; v <<= 8;
v |= bytes[7]; v <<= 8;
v |= bytes[8];
return (int64_t)v;
}
int main() {
uint8_t bytes[8] = {0};
int64_t val = 1234567890;
//Print value to be received on the other side
std::cout << std::dec << "INPUT: " << val << std::endl;
//Serialize
memcpy(&bytes, int64ToBytes(val).data(), 8);
//Deserialize
int64_t val2 = bytesToInt64(bytes);
//print deserialized int64
std::cout << std::dec << "RESULT: " << val2 << std::endl;
}
Output:
INPUT: 1234567890
RESULT: 316049379840
Been trying to solve this for a day now, cant find the problem
Thanks.
Try using the uint64_t htobe64(uint64_t host_64bits) and uint64_t be64toh(uint64_t big_endian_64bits) functions to convert from host to big endian (network order) and from network order to host order respectively.
You are shifting the entire value. Try something like:
(bytes[0] << 56) |
(bytes[1] << 48) |
... (bytes[7])
There is no 9th byte (ie. byte[8]).
you are missing a bit shift in the bytesToInt64 function:
below you find the corrected bytesToInt64 function:
int64_t bytesToInt64(uint8_t bytes[8])
{
uint64_t v = 0;
v |= bytes[0]; v <<= 8;
v |= bytes[1]; v <<= 8;
v |= bytes[2]; v <<= 8;
v |= bytes[3]; v <<= 8;
v |= bytes[4]; v <<= 8;
v |= bytes[5]; v <<= 8;
v |= bytes[6]; v <<= 8;
v |= bytes[7];
return (int64_t)v;
}
If you're transferring data between machines with the same endianness you don't need to serialize the data byte by byte, you can just send the data as it is represented in memory. In this case you don't need anything like that you can just use your memcpy call like this:
// Serialize
memcpy(&bytes, &val, sizeof(val));
// Deserialize
int64_t val2;
memcpy(&val2, &bytes, sizeof(val));
If you're sending data between hosts with different endianness you should send it as you find it in the aswer from Roger, basically you have to make sure the data is represented in the same way on both ends.
here's a variant which not only serializes but will work with any type of int and across any platforms
#include <iostream>
#include <type_traits>
using namespace std;
template <typename T> enable_if_t<is_integral_v<T>> serialize(T t, char *buf)
{
for(auto i = 0U; i < sizeof(t); ++i) {
buf[i] = t & 0xff;
t >>= 8;
}
}
template <typename T> enable_if_t<is_integral_v<T>> deserialize(T &t, char const *buf)
{
for(auto i = 0U; i < sizeof(t); ++i) {
t <<= 8;
t |= buf[sizeof(t) - 1 - i];
}
}
int main() {
int64_t t1 = 0x12345678;
int64_t t2{0};
char buffer[sizeof(t1)];
serialize(t1, buffer);
deserialize(t2, buffer);
cout << "I got " << hex << t2 << endl;
}
you should probably use containers and parts to serialize/deserialize data to make sure you don't overflow your buffer (considering you are transferring more than one int at a time)
This should work. You may also need to check the input array is the right size in your bytesToInt64 function.
std::array<uint8_t, 8> int64ToBytes(int64_t val)
{
uint64_t v = (uint64_t)val;
std::array<uint8_t, 8> bytes;
for (size_t i = 0; i < 8; i++)
{
bytes[i] = (v >> (8 * (7 - i))) & 0xFF;
}
return bytes;
}
int64_t bytesToInt64(uint8_t bytes[8])
{
uint64_t v = 0;
for (size_t i = 0; i < 8; i++)
{
v |= (bytes[i] << (8 * (7 - i)));
}
return (int64_t)v;
}

Copy n Bits from 8-bit Array into 64-bit Integer?

I am trying to copy n bits from any position of an array of uint8_ts into a single 64 bit integer. Here is a working solution that can copy an arbitrary amount of bits into a 64 bit integer starting at the beginning of the array, but I want to be able to start at any position of the array.
For example I might want to copy bits 2 through 11 of the array:
{7, 128, 7}
In binary that would be:
00000111 1000000 00000111
And I want an integer with value:
0001111000
std::uint64_t key_reg(std::uint8_t* bytes, std::size_t n)
{
std::uint64_t reg = 0;
// The amount of bits that fit into an entire element of an array
// ex, if I'm copying 17 bits, even_bytes == 2
std::size_t even_bytes = (n - (n % 8)) / 8;
// what's left over after the even bytes
// in this case, remainder == 1
std::size_t remainder = n - even_bytes * 8;
// copy each byte into the integer
for(std::size_t i = 0; i < even_bytes; ++i)
if(remainder)
reg |= (std::uint64_t)bytes[i] << (8 * (even_bytes - i));
else
reg |= (std::uint64_t)bytes[i] << (8 * (even_bytes - i - 1));
// if there is an uneven number of bits, copy them in
if(remainder)
reg |= (std::uint64_t)bytes[even_bytes];
return reg;
}
Do you have any idea how to implement
std::uint64_t key_reg(std::uint8_t* bytes, std::size_t pos, std::size_t n);
I didn't think anyone would answer so fast, so here was a solution I came up with in the same style. I found this bitfieldmask function on stackoverflow, but I'm unable to find the question to credit the author.
template<typename R>
static constexpr R bitfieldmask(unsigned int const a, unsigned int const b)
{
return ((static_cast<R>(-1) >> (((sizeof(R) * CHAR_BIT) - 1) - (b)))
& ~((1 << (a)) - 1));
}
std::uint64_t key_reg(std::uint8_t* bytes, std::size_t pos, std::size_t n)
{
std::uint64_t reg = 0;
std::size_t starting_byte = (pos < 8) ? 0 : ((pos - (pos % 8)) / 8);
std::size_t even_bytes = (n - (n % 8)) / 8;
std::size_t remainder = n - even_bytes * 8;
for(std::size_t i = 0; i < even_bytes; ++i)
if(remainder)
reg |= (std::uint64_t)bytes[starting_byte + i] << (8 * (even_bytes - i));
else
reg |= (std::uint64_t)bytes[starting_byte + i] << (8 * (even_bytes - i - 1));
if(remainder)
reg |= (std::uint64_t)bytes[even_bytes];
// mask out anything before the first bit
if(pos % 8 != 0) {
std::size_t a = n - pos;
std::size_t b = n;
auto mask = bitfieldmask<std::uint64_t>(a, b);
reg = (reg & ~mask);
}
return reg;
}
I think it is just simpler to copy all necessary bytes and then mask extra bits:
std::uint64_t key_reg(std::uint8_t* bytes, std::size_t n)
{
std::uint64_t reg = 0;
std::reverse_copy( bytes, bytes + n / 8 + ( n % 8 != 0 ),
reinterpret_cast<char *>( &reg ) );
reg >>= n % 8;
reg &= ~( -1UL << n );
return reg;
}
using pos would be little more complex:
std::uint64_t key_reg(std::uint8_t* bytes, std::size_t pos, std::size_t n)
{
std::uint64_t reg = 0;
auto endpos = pos + n;
auto start = bytes + pos / 8;
auto end = bytes + endpos / 8 + ( endpos % 8 != 0 );
std::reverse_copy( start, end, reinterpret_cast<char *>( &reg ) );
reg >>= endpos % 8;
reg &= ~( -1UL << n );
return reg;
}
live example
Your basic approach looks sound. To handle bit offsets that aren't multiples of 8, you just need to first read in a single partial byte and then proceed with the rest:
uint64_t key_reg(const uint8_t* bytes, size_t pos, size_t n) {
const uint8_t* ptr = bytes + pos / 8;
uint64_t result = 0;
if (pos % 8 > 0) {
/* read the first partial byte, masking off unwanted bits */
result = *(ptr++) & (0xFF >> (pos % 8));
if (n <= 8 - pos % 8) {
/* we need no more bits; shift off any excess and return early */
return result >> (8 - pos % 8 - n);
} else {
/* reduce the requested bit count by the number we got from this byte */
n -= 8 - pos % 8;
}
}
/* read and shift in as many whole bytes as we need */
while (n >= 8) {
result = (result << 8) + *(ptr++);
n -= 8;
}
/* finally read and shift in the last partial byte */
if (n > 0) {
result = (result << n) + (*ptr >> (8-n));
}
return result;
}
Here's an online demo with a simple test harness, demonstrating that this code indeed works correctly in all the edge cases I could find, such as reading a full 64 bits starting from the middle of a byte or reading only part of a single byte (which is actually a non-trivial special case, handled in a separate branch with its own return statement in the code above).
(Note that I wrote the code above in plain C since, like your original code, it doesn't really make use of any C++ specific features. Feel free to "C++ify" it by adding std:: where appropriate.)
One feature that the test harness doesn't check, but which I believe this code should possess, is that it never reads more bytes from the input array than necessary. In particular, the bytes array is not accessed at all if n == 0 (although a pointer to pos / 8 bytes after the start of the array is still calculated).
I have the following
struct MyType
{
std::array<uint8_t, 892> m_rguID;
uint16_t m_bitLength;
void GetBits(uint16_t startBit, uint16_t nBits, uint64_t & bits) const
};
void MyType::GetBits(uint16_t startBit, uint16_t nBits, uint64_t & bits) const
{
if(startBit + nBits > m_bitLength)
throw std::runtime_error("Index is out of range");
uint32_t num1 = startBit % 8U;
uint32_t num2 = 8U - num1;
uint32_t num3 = nBits >= num2 ? num2 : nBits;
uint32_t num4 = startBit >> 3;
bits = (uint64_t)(((int64_t)((uint64_t)m_rguID[num4] >> (8 - num3 - num1)) & (int64_t)((1 << num3) - 1)) << (nBits - num3));
uint32_t num5 = num4 + 1U;
int num6 = nBits - num3;
if(num6 <= 0)
return;
int num7 = num6 - 8;
int num8 = 8 - num6;
do
{
if(num6 >= 8)
{
bits |= (uint64_t)m_rguID[num5] << num7;
++num5;
}
else
{
bits |= (uint64_t)m_rguID[num5] >> num8;
++num5;
}
num6 += -8;
num7 += -8;
num8 += 8;
} while(num6 > 0);
}

Substitute an instruction depending on a condition

I have two for loops that I want to write in a function as one. The problem is that it differ only in one instruction
for (int i = 1; i <= fin_cabecera - 1 ; i++ ){
buffer[i] &= 0xfe;
if (bitsLetraRestantes < 0) {
bitsLetraRestantes = 7;
mask = 0x80;
letra = sms[++indiceLetra]; //*differs here*
}
char c = (letra & mask) >> bitsLetraRestantes--;
mask >>= 1;
buffer[i] ^= c;
}
And the other
for (int i = datos_fichero; i <= tamanio_en_bits + datos_fichero; i++){
buffer[i] &= 0xfe;
if (bitsLetraRestantes < 0) {
bitsLetraRestantes = 7;
mask = 0x80;
f.read(&letra, 1); //*differs here*
}
char c = (letra & mask) >> bitsLetraRestantes--;
mask >>= 1;
buffer[i] ^= c;
}
I thought in something like this:
void write_bit_by_bit(unsigned char buffer[], int from, int to, bool type) {
for (int i = to; i <= from; i++) {
buffer[i] &= 0xfe;
if (bitsLetraRestantes < 0) {
bitsLetraRestantes = 7;
mask = 0x80;
type ? (letra = sms[++indiceLetra]) : f.read(&letra, 1);
}
char c = (letra & mask) >> bitsLetraRestantes--;
mask >>= 1;
buffer[i] ^= c;
}
}
But I think there has to be a better method.
Context:
I will give more context (I will try explain it as better as I can within my language limitations). I have to read one byte each time because The Buffer variable represents a image pixel. sms is a message that have to be hidden within the image, and letra is a single char of that message. In order to not modify the aspect of the image, each bit of each character have to be written in the last bit of each pixel. Let me give you and example.
letra = 'H' // 01001000 in binary
buffer[0] = 255 // white pixel 11111111
In order to hide the H char, I will need 8 pixel:
The result will be like:
buffer[0] //11111110,
buffer[1] //11111111
buffer[2] //11111110
buffer[3] //11111110
buffer[4] //11111111
buffer[5] //11111110
buffer[6]//11111110
buffer[7]//11111110
The H is hidden in the last bit of the image. I hope I explained well.
[Solution]
Thanks to #anatolyg I've rewrited the code and now works just as I wanted. Here is how it looks:
void write_bit_by_bit(unsigned char buffer[], ifstream& f,int from, int to, char sms[], bool type){
unsigned short int indiceLetra = 0;
short int bitsLetraRestantes = 7;
unsigned char mask = 0x80; //Empezamos por el bit más significativo (10000000)
char* file_buffer;
if(type){ //Write file data
int number_of_bytes_to_read = get_file_size(f);
file_buffer = new char[number_of_bytes_to_read];
f.read(file_buffer, number_of_bytes_to_read);
}
const char* place_to_get_stuff_from = type ? file_buffer : sms;
char letra = place_to_get_stuff_from[0];
for (int i = from; i <= to; i++) {
buffer[i] &= 0xfe; //hacemos 0 último bit con máscara 11111110
//TODO: Hacer con dos for
if (bitsLetraRestantes < 0) {
bitsLetraRestantes = 7;
mask = 0x80;
letra = place_to_get_stuff_from[++indiceLetra];//letra = sms[++indiceLetra];
}
char c = (letra & mask) >> bitsLetraRestantes--;
mask >>= 1;
buffer[i] ^= c; //Almacenamos en el ultimo bit del pixel el valor del caracter
}
}
int ocultar(unsigned char buffer[],int tamImage, char sms[], int tamSms){
ifstream f(sms);
if (f) {
strcpy(sms,basename(sms));
buffer[0] = 0xff;
int fin_cabecera = strlen(sms)*8 + 1;
buffer[fin_cabecera] = 0xff;
write_bit_by_bit(buffer, f, 1, fin_cabecera -1, sms, WRITE_FILE_NAME);
int tamanio_en_bits = get_file_size(f) * 8;
int datos_fichero = fin_cabecera + 1;
write_bit_by_bit(buffer, f, datos_fichero, tamanio_en_bits + datos_fichero, sms, WRITE_FILE_DATA);
unsigned char fin_contenido = 0xff;
short int bitsLetraRestantes = 7;
unsigned char mask = 0x80;
for (int i = tamanio_en_bits + datos_fichero + 1;
i < tamanio_en_bits + datos_fichero + 1 + 8; i++) {
buffer[i] &= 0xfe;
char c = (fin_contenido & mask) >> bitsLetraRestantes--;
mask >>= 1;
buffer[i] ^= c;
}
}
return 0;
}
Since you are talking about optimization here, consider performing the read outside the loop. This will be a major optimization (reading 10 bytes at once must be quicker than reading 1 byte 10 times). This will require an additional buffer for (the file?) f.
if (!type)
{
char f_buffer[ENOUGH_SPACE];
number = calc_number_of_bytes_to_read();
f.read(f_buffer, number);
}
for (...) {
// your code
}
After you have done this, your original question is easy to answer:
const char* place_to_get_stuff_from = type ? sms : f_buffer;
for (...) {
...
letra = place_to_get_stuff_from[++indiceLetra];
...
}