I've come across this answer.
But I hit 2 issues, I'm not sure how to adapt it to pack 64 bits (see below), and I can't get my head around unpacking them.
Here is what I have for packing:
const int i = 1;
#define is_bigendian() ((*(char *) &i) == 0)
#define MAGIC (is_bigendian() ? 0x0102040810204080 : 0x8040201008040201)
inline uint8_t pack8b(bool *a) {
uint64_t t = *((uint64_t *) a);
return (MAGIC * t >> 56) & 0xFF;
}
uint32_t pack32b(bool *a) {
return (pack8b(a + 0) << 24) | (pack8b(a + 8) << 16) |
(pack8b(a + 16) << 8) | (pack8b(a + 24) << 0);
}
uint64_t pack64b(bool *a) {
return ((uint64_t) pack32b(a) << 32) | pack32b(a + 32);
}
EDIT: Here is what I have so far that works like a charm, i'm open to suggestion for performance improvement:
uint64_t MAGIC() {
static const int i = 1;
// Takes of little / big endian
bool bigEndian = ((*(char *) &i) == 0);
return bigEndian ? 0x0102040810204080 : 0x8040201008040201;
}
uint8_t pack8b(bool *a) {
uint64_t t = *((uint64_t *) a);
return (MAGIC() * t >> 56) & 0xFF;
}
uint16_t pack16b(bool *a) {
return ((uint16_t) pack8b(a + 0) << 8) | (pack8b(a + 8) << 0);
}
uint32_t pack32b(bool *a) {
return ((uint32_t) pack16b(a + 0) << 16) | (pack16b(a + 16) << 0);
}
uint64_t pack64b(bool *a) {
return ((uint64_t) pack32b(a + 0) << 32) | (pack32b(a + 32) << 0);
}
void unpack8b(bool *a, uint8_t v) {
uint64_t mask = 0x8080808080808080ULL;
*((uint64_t *) a) = ((MAGIC() * v) & mask) >> 7;
}
void unpack16b(bool *a, uint16_t v) {
unpack8b(a + 0, v >> 8);
unpack8b(a + 8, v >> 0);
}
void unpack32b(bool *a, uint32_t v) {
unpack16b(a + 0, v >> 16);
unpack16b(a + 16, v >> 0);
}
void unpack64b(bool *a, uint64_t v) {
unpack32b(a + 0, v >> 32);
unpack32b(a + 32, v >> 0);
}
Your solution works for me I tested it with the gcc on tio: Try it online!
The simplest function for unpacking the uint64_t would be:
void unpack64b(uint64_t num, bool* bit_field) {
for (int i = 63; 0 <= i; i--) {
bit_field[i] = num & 1;
num = num >> 1;
}
}
If bit_field is initialized to all zeros then early exiting is possible:
void unpack64b(uint64_t num, bool* bit_field) {
for (int i = 63; num > 0; i--) {
bit_field[i] = num & 1;
num = num >> 1;
}
}
To the packing for 32 and 64 bit, shouldn't this also be aware of the endian encoding?
I can't get my head around unpacking them.
For starters, concentrate on just iterating the bits, do a simplest method that works. For me that was just simple array assignments with bit-and mask:
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
static const int i = 1;
uint64_t MAGIC(void) {
return ((*(const char *)&i) == 0) ? 0x0102040810204080ull : 0x8040201008040201ull;
}
void unpack8b(bool a[8], uint8_t v) {
if (((*(const char *)&i) == 0)) {
a[0] = v & 0x01;
a[1] = v & 0x02;
a[2] = v & 0x04;
a[3] = v & 0x08;
a[4] = v & 0x10;
a[5] = v & 0x20;
a[6] = v & 0x40;
a[7] = v & 0x80;
} else {
a[7] = v & 0x01;
a[6] = v & 0x02;
a[5] = v & 0x04;
a[4] = v & 0x08;
a[3] = v & 0x10;
a[2] = v & 0x20;
a[1] = v & 0x40;
a[0] = v & 0x80;
}
}
void unpack16b(bool a[16], uint16_t v) {
unpack8b(&a[0], v >> 8);
unpack8b(&a[8], v);
}
void unpack32b(bool a[32], uint32_t v) {
unpack16b(&a[0], v >> 16);
unpack16b(&a[16], v);
}
void unpack64b(bool a[64], uint64_t v) {
unpack32b(&a[0], v >> 32);
unpack32b(&a[32], v);
}
uint8_t pack8b(bool a[8]) {
static_assert(sizeof(bool) == 1, "");
static_assert(sizeof(uint64_t) == 8, "");
static_assert(CHAR_BIT == 8, "");
uint64_t t;
memcpy(&t, a, sizeof(t));
return (MAGIC() * t >> 56) & 0xFF;
}
uint16_t pack16b(bool a[16]) {
return pack8b(&a[0]) << 8 | pack8b(&a[8]);
}
uint32_t pack32b(bool a[32]) {
return (uint32_t)pack16b(&a[0]) << 16 | pack16b(&a[16]);
}
uint64_t pack64b(bool a[64]) {
return ((uint64_t)pack32b(&a[0]) << 32) | pack32b(&a[32]);
}
int main() {
_Alignas(uint64_t) bool a[64];
for (int i = 0; i < 64; ++i) {
a[i] = rand() % 2;
}
for (int i = 0; i < 64; ++i) printf("%d%s", a[i], !((i+1)%8)?" ":"");
printf("\n");
uint64_t v = pack64b(a);
printf("%llx\n", v);
memset(a, -1, sizeof(a));
unpack64b(a, v);
for (int i = 0; i < 64; ++i) printf("%d%s", a[i], !((i+1)%8)?" ":"");
printf("\n");
}
Outputs:
10111100 11010110 00001011 00011110 00111010 11110100 10101001 00011101
bcd60b1e3af4a91d
10111100 11010110 00001011 00011110 00111010 11110100 10101001 00011101
Note that when int on a platform has 16 bits, the (pack8b(a + 0) << 24) in your code is undefined behavior. Cast it to type with more bits then 24 (uint32_t)pack8b(a + 0) << 24 before shifting to be extra safe.
Related
I can rotate a word to left or right by a certain amount like this:
#define ROR(x, r) ((x >> r) | (x << (64 - r)))
#define ROL(x, r) ((x << r) | (x >> (64 - r)))
[...]
ROR(var1, 11);
ROL(var1, 11);
How can I do the same but with an entire array of bytes (I mean: all the bits in array sequence)? An array like this:
uint32_t somearray[12] = {
0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96,
0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,
};
PS: There is a similar question here, but I need to know how to do it with some amount.
There are 2 problems with the ROR and ROL macros:
the macro assumes that the argument x is a uint64_t.
shifting a uint64_t by 64 positions has undefined behavior. If the number of shifts r can be null, you should modify the expression to avoid shifting by 64.
the macro arguments should be parenthesized in the expansion to avoid precedence issues.
Here is a modified version:
// assuming x is a uint64_t and r in the range 0..63
#define ROR64(x, n) (((x) >> (n)) | ((x) << (63 - (n)) << 1))
#define ROL64(x, n) (((x) << (n)) | ((x) >> (63 - (n)) >> 1))
Note that it is recommended to use inline functions instead of macros to avoid problems with operands with side effects and enforce operand sizes:
// assuming r in the range 0..63
static inline uint64_t ror64(uint64_t x, int n) {
return (x >> n) | (x << (63 - n) << 1);
}
static inline uint64_t rol64(uint64_t x, int n) {
return (x << n) | (x >> (63 - n) >> 1);
}
To rotate a full array of words, it is simpler to use a different array as the source and destination and write a loop:
void ror32_array(uint32_t *dst, const uint32_t *src, size_t size, size_t n) {
size_t dist = n / 32 % size;
int shift = n % 32;
for (size_t i = 0; i < size; i++) {
dst[(i + dist) % size] = src[i] >> shift;
}
if (shift) {
for (size_t i = 0; i < size; i++) {
dst[(i + dist + 1) % size] |= src[i] << (32 - shift);
}
}
}
void rol32_array(uint32_t *dst, const uint32_t *src, size_t size, size_t n) {
size_t dist = n / 32 % size;
int shift = n % 32;
for (size_t i = 0; i < size; i++) {
dst[(i + size - dist) % size] = src[i] << shift;
}
if (shift) {
for (size_t i = 0; i < size; i++) {
dst[(i + size - dist - 1) % size] |= src[i] >> (32 - shift);
}
}
}
Here is a test program:
#include <stdarg.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
[...]
void print(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
while (*fmt) {
if (*fmt == '%') {
if (fmt[1] == 'd') {
printf("%d", va_arg(ap, int));
fmt += 2;
continue;
} else
if (fmt[1] == 'p') {
const uint32_t *src = va_arg(ap, uint32_t *);
size_t size = va_arg(ap, size_t);
for (size_t i = 0; i < size; i++) {
printf("%08lX%c", (long)src[i], " "[i + 1 == size]);
}
fmt += 2;
continue;
}
}
putchar(*fmt++);
}
va_end(ap);
}
int main() {
uint32_t array[] = { 0, 1, 2, 4, 8, 16 };
size_t size = sizeof(array) / sizeof(*array);
uint32_t dest_array[size];
int i, n, shift[] = { 0, 1, 2, 3, 8, 15, 24, 32, 48, 64, -1 };
print("array = { %p }\n", array, size);
for (i = 0; (n = shift[i]) >= 0; i++) {
ror32_array(dest_array, array, size, n);
memcpy(array, dest_array, sizeof(array));
print("ror(array, %d) = { %p }\n", n, array, size);
}
while ((n = shift[--i]) != 0) {
rol32_array(dest_array, array, size, n);
memcpy(array, dest_array, sizeof(array));
print("rol(array, %d) = { %p }\n", n, array, size);
}
return 0;
}
To rotate all the bits in an array, create a function that takes a size, a pointer to the data, and a shift amount.
void rotate_right(size_t sz, void *a, unsigned shift);
#define ROR_ARRAY(a, sh) rotate_right((sizeof (a)), (a), (sh))
For an array of 32-bit values, the bit shift of the array may be be the same arithmetically as some_32_bit >> sh due to endian. More advanced use of macros with _Generic solve solve that.
I am following a painless guide to code correction algorithms. (https://zlib.net/crc_v3.txt) I've managed to write a TABLE algorithm, using extra loop for augmented part (I hope so). I am trying to write a most widely used CRC32 version (with 0x04C11DB7 polynomial), but I can not get the right CRC value.
I've achieved the correct table for CRC32 values with mentioned polynomial.
My code for generating CRC32 (chapter 9 and 10):
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#define CRC32_BYTE_POSSIBLE_VALUES 255
#define CRC32_LAST_BIT_MASK 0x80000000
#define CRC32_POLYNOMIAL 0x04C11DB7
uint32_t __crc32_table[CRC32_BYTE_POSSIBLE_VALUES] = { 0 };
void __crc32_fill_crc_table() {
uint32_t reg;
uint8_t byte = 0;
for (;;) {
reg = (byte << 24);
for (uint8_t byte_size = 0; byte_size < 8; byte_size++) {
if (reg & CRC32_LAST_BIT_MASK) {
reg <<= 1;
reg ^= CRC32_POLYNOMIAL;
} else {
reg <<= 1;
}
}
__crc32_table[byte] = reg;
if (byte == 255)
break;
else
byte++;
}
}
void __crc32_print_table(uint32_t *arr) {
printf(" 0x%08X ", arr[0]);
for (uint32_t i = 1; i < 256; i++) {
if (!(i % 8))
printf("\n");
printf(" 0x%08X ", arr[i]);
}
printf("\n");
}
uint8_t inverse_byte(uint8_t byte) {
uint8_t reflected_byte = 0;
for (uint8_t i = 0; i < 8; i++) {
if (byte & (1 << i))
reflected_byte |= (1 << (7 - i));
}
return reflected_byte;
}
uint32_t inverse(uint32_t src) {
uint32_t toret;
for (uint8_t i = 0; i < 32; i++) {
if (src & (1 << i))
toret |= (1 << (31 - i));
}
return toret;
}
uint32_t __crc32_table_approach( unsigned char *data, size_t size) {
uint32_t reg = -1;
uint8_t top_byte;
for (size_t i = 0; i < size; i++) {
top_byte = (uint8_t)(reg >> 24);
reg = (reg << 8) | inverse_byte(data[i]);
reg ^= __crc32_table[top_byte];
}
for (size_t i = 0; i < 4; i++) {
top_byte = (uint8_t) (reg >> 24);
reg = (reg << 8) ;
reg ^= __crc32_table[top_byte];
}
return inverse(reg) ^ -1;
}
uint32_t calc_crc32(unsigned char *data, size_t size) {
if (!__crc32_table[1])
__crc32_fill_crc_table();
__crc32_print_table(__crc32_table);
return __crc32_table_approach(data, size);
}
int main( int argc, char** argv )
{
unsigned char* test = "123456789";
size_t test_len = strlen(test);
uint32_t crc = calc_crc32(test, test_len);
printf("CRC32: 0x%08X", crc);
return 0;
}
The inverse function reverses bits of UINT32 value, and function inverse_byte inverses bits of UINT8 value.
But for the '123456789' string I get the wrong checksum.
Could someone help me? Or give some advice?
Input string: '123456789'
Outputted CRC: CRC32: 0x22016B0A
Desired CRC: CRC32: 0xCBF43926
You made your array one word too short, and so overwrote the allocated memory. It needs to be:
#define CRC32_BYTE_POSSIBLE_VALUES 256
Though that part probably still worked, because C.
You need to initialize the variable you are reversing into:
uint32_t toret = 0;
These lines:
top_byte = (uint8_t)(reg >> 24);
reg = (reg << 8) | inverse_byte(data[i]);
need to be:
top_byte = (uint8_t)(reg >> 24) ^ inverse_byte(data[i]);
reg <<= 8;
and you need to delete these lines:
for (size_t i = 0; i < 4; i++) {
top_byte = (uint8_t) (reg >> 24);
reg = (reg << 8) ;
reg ^= __crc32_table[top_byte];
}
Then you get the right answer.
If you would like to implement the table approach on the augmented message as described in Chapter 9 (requiring another four iterations of the CRC at the end as in your code), then you need to first read these important notes in the document:
Note: The initial register value for this algorithm must be the
initial value of the register for the previous algorithm fed through
the table four times. Note: The table is such that if the previous
algorithm used 0, the new algorithm will too.
To get the same effect as the initial value of 0xffffffff (notably not zero) with the non-augmented message version, which is how that standard CRC is defined, then you'd need to find an initial value such that applying 32 zero bits to it using the CRC gives you 0xffffffff. That value is 0x46af6449, obtained by reversing the CRC bit-wise algorithm:
uint32_t x = -1;
for (unsigned i = 0; i < 32; i++)
x = x & 1 ? ((x ^ 0x4c11db7) >> 1) | 0x80000000 : x >> 1;
Then your code will work if you fix the array size and the toret initialization errors, and simply replace:
uint32_t reg = -1;
with:
uint32_t reg = 0x46af6449;
Either augmented or not, reversing every single input byte as you are doing is a waste of time. You can and should instead just reverse the calculation and the polynomial. See rcgldr's answer.
Example code using right shifting CRC (0xedb88320 is a reflected version of 0x04C11DB7):
#include <iostream>
#include <iomanip>
typedef unsigned char uint8_t;
typedef unsigned int uint32_t;
uint32_t crctbl[256];
void gentbl(void)
{
uint32_t crc;
uint32_t c;
uint32_t i;
for(c = 0; c < 0x100; c++){
crc = c;
for(i = 0; i < 8; i++){
crc = (crc & 1) ? (crc >> 1) ^ 0xedb88320 : (crc >> 1);
}
crctbl[c] = crc;
}
}
uint32_t crc32(uint8_t * bfr, size_t size)
{
uint32_t crc = 0xfffffffful;
while(size--)
crc = (crc >> 8) ^ crctbl[(crc & 0xff) ^ *bfr++];
return(crc ^ 0xfffffffful);
}
int main(int argc, char**argv)
{
uint32_t crc;
uint8_t msg[10] = "123456789";
gentbl();
crc = crc32(msg, 9);
std::cout << "crc " << std::hex << std::setw(8) << std::setfill('0') << crc << std::endl;
return(0);
}
Most implementations of SHA-1 (even on Wikipedia) I came across are coded for big-endian runtimes. So I'm trying to implement my own version for my machine (little-endian).
I've followed the pseudo-code form Wikipedia and have the following code. I found a function that converts the byte ordering but still not getting the correct output.
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#define rotateleft(x,n) ((x<<n) | (x>>(32-n)))
unsigned int endian_reverse(unsigned int n)
{
unsigned int m = 0;
m |= n << 24;
m |= ((n >> 8) << 24) >> 8;
m |= ((n << 8) >> 24) << 8;
m |= n >> 24;
return m;
}
void SHA1(unsigned char * str1)
{
unsigned long int h0,h1,h2,h3,h4,a,b,c,d,e,f,k,temp;
h0 = 0x67452301;
h1 = 0xEFCDAB89;
h2 = 0x98BADCFE;
h3 = 0x10325476;
h4 = 0xC3D2E1F0;
unsigned char * str;
str = (unsigned char *)malloc(strlen((const char *)str1)+100);
strcpy((char *)str,(const char *)str1);
int current_length = strlen((const char *)str);
int original_length = current_length;
str[current_length] = 0x80;
str[current_length + 1] = '\0';
char ic = str[current_length];
current_length++;
int ib = current_length % 64;
int i, j;
if(ib<56)
ib = 56-ib;
else
ib = 120 - ib;
for(i=0;i < ib;i++)
{
str[current_length]=0x00;
current_length++;
}
str[current_length + 1]='\0';
for(i=0;i<6;i++)
{
str[current_length]=0x0;
current_length++;
}
str[current_length] = (original_length * 8) / 0x100 ;
current_length++;
str[current_length] = (original_length * 8) % 0x100;
current_length++;
str[current_length+i]='\0';
int number_of_chunks = current_length/64;
unsigned long int word[80];
for(i=0;i<number_of_chunks;i++)
{
for(j=0;j<16;j++)
{
word[j] =
str[i*64 + j*4 + 0] * 0x1000000 + str[i*64 + j*4 + 1] * 0x10000 +
str[i*64 + j*4 + 2] * 0x100 + str[i*64 + j*4 + 3];
}
for(j=16;j<80;j++)
{
word[j] = rotateleft((word[j-3] ^ word[j-8] ^ word[j-14] ^ word[j-16]),1);
}
a = h0;
b = h1;
c = h2;
d = h3;
e = h4;
for(int m=0;m<80;m++)
{
if(m<=19)
{
f = (b & c) | ((~b) & d);
k = 0x5A827999;
}
else if(m<=39)
{
f = b ^ c ^ d;
k = 0x6ED9EBA1;
}
else if(m<=59)
{
f = (b & c) | (b & d) | (c & d);
k = 0x8F1BBCDC;
}
else
{
f = b ^ c ^ d;
k = 0xCA62C1D6;
}
temp = (rotateleft(a,5) + f + e + k + word[m]) & 0xFFFFFFFF;
e = d;
d = c;
c = rotateleft(b,30);
b = a;
a = temp;
}
h0 = h0 + a;
h1 = h1 + b;
h2 = h2 + c;
h3 = h3 + d;
h4 = h4 + e;
}
h0 = endian_reverse(h0);
h1 = endian_reverse(h1);
h2 = endian_reverse(h2);
h3 = endian_reverse(h3);
h4 = endian_reverse(h4);
printf("\n\n");
printf("Hash: %x %x %x %x %x",h0, h1, h2, h3, h4);
printf("\n\n");
}
int main()
{
SHA1((unsigned char *)"abc");
return 0;
}
SHA-1 ("abc"):
Resulting
Hash: f7370736 e388302f 15815610 1ccacd49 e7649bb6
Correct (actual)
Hash: a9993e36 4706816a ba3e2571 7850c26c 9cd0d89d
Am I doing my endianness conversion correctly or in the correct spot?
Am I doing my endianness conversion correctly or in the correct spot?
Endianness conversion endian_reverse(() is incorrect when unsigned is not 32-bit.
Endianness conversion not used in correct spot. Endian conversion not needed.
Other issues exists.
Code is assuming unsigned long int is 32-bit. When unsigned long int is 64-bit, I can get the same answer as OP.
Save yourself time: There is no reason to use loose types here. Use uint32_t, uint8_t. For array sizing and string lengths use size_t. Avoid signed types and constants.
By changing unsigned long --> uint32_t and dropping h0 = endian_reverse(h0); ... h7 = endian_reverse(h7); I came up with
Hash: a9993e36 4706816a ba3e2571 7850c26c 9cd0d89d
Other suggestions.
Multiple of 64
size in str = malloc(size) should be a multiple of 64
Stay within multiples of 64
str[current_length+i]='\0'; can write outside allocation.
Alternate size storing
Works for all size_t values up to 264-3 - 1.
size_t current_length = ...
// append length in bits
uint64_t current_length_bits = current_length;
current_length_bits *= 8;
for (i = 8; i > 0; ) {
i--;
str[current_length + i] = (unsigned char) current_length_bits;
current_length_bits >>= 8;
}
current_length += 8;
How can I create a bit-field array with variable size?
The following code is what I tried, which didn't work.
#include <stdio.h>
int main()
{
int n=4;
struct bite{
unsigned a1:2;
unsigned a2:2;
:
:
unsigned a(n-1):2;
unsigned a(n):2;
}bits;
for(i=1;i<=n;i++)
bits.a[i]=i;
for(i=1;i<=n;i++)
printf("%d ",bits.a[i]);
return 0;
}
The members of a struct cannot be defined at runtime.
You could simulate a bit array using a char array and some macros.
#define BitArray(array, bits) \
unsigned char array[bits / 8 + 1]
#define SetBit(array, n) \
do { array[n / 8] |= (1 << (n % 8)) } while (0)
#define GetBit(array, n) \
((array[n / 8] >> (n % 8)) & 1)
int main(void)
{
BitArray(bits, 42); /* Define 42 bits and init to all 0s
(in fact this allocates memory for (42/8 + 1) * 8 bits). */
SetBit(bits, 2); /* Set bit 2. */
int bit2 = GetBit(bits, 2); /* Get bit 2 */
...
Similar for 2-bit words are per your code:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define Define2BitWordArray(array, two_bit_words) \
unsigned char array[two_bit_words / 4 + 1]
#define Set2BitWord(array, n, value) \
do { array[n / 4] |= (unsigned char)((value & 0x11) << (2 * (n % 4))); } while (0)
#define Get2BitWord(array, n) \
((array[n / 4] >> (2 * (n % 4))) & 0x11)
int main(void)
{
size_t n = 10;
Define2BitWordArray(bits, n); /* Define 10 two-bits words
(in fact this allocates memory
for (10/4 + 1) * 4 two-bit bits). */
memset(bits, 0, sizeof bits); /* Set all bits to 0. */
for(size_t i = 0; i < n; i++) /* C array's indexes are 0-based. */
{
Set2BitWord(bits, i, i);
}
for(size_t i = 0; i < n; i++)
{
printf("%d ", Get2BitWord(bits, i));
}
}
Remember your array always starts at 0 position.
You can do this:
#include <stdio.h>
typedef struct
{
unsigned int x : 1;
} one;
int main() {
int n = 10;
one xx[n];
int i;
for(i=0;i<n;i++)
xx[i].x=i;
for(i=0;i<n;i++)
printf("%d ",xx[i].x);
return 0;
}
if you want to operate on the particular bit or bits on data which has not limited size (actually limited to the max value of the size_t divided by 8). Little endian in this example
void setbit(void *obj, size_t bit)
{
uint8_t *p = obj;
p[bit >> 3] |= (1 << (bit & 7));
}
void resetbit(void *obj, size_t bit)
{
uint8_t *p = obj;
p[bit >> 3] &= ~(1 << (bit & 7));
}
void assignbit(void *obj, size_t bit, unsigned value)
{
uint8_t *p = obj;
p[bit >> 3] &= ~(1 << (bit & 7));
p[bit >> 3] != (!!value << (bit & 7));
}
void setbits(void *obj, size_t bit, size_t num)
{
while (num--)
setbit(obj, bit + num);
}
void resetbits(void *obj, size_t bit, size_t num)
{
while (num--)
resetbit(obj, bit + num);
}
void assignbits_slow(void *obj, size_t pos, size_t size, unsigned value)
{
for (size_t i = 1, j = 0; j < size; j++, i <<= 1)
assignbit(obj, pos + j, !!(value & i));
}
int getbit(void *obj, size_t bit)
{
uint8_t *p = obj;
return !!(p[bit >> 3] & (1 << (bit & 7)));
}
void *getbits(void *obj, void *buff, size_t bit, size_t nbits)
{
memset(buff, 0, nbits >> 3 + !!(nbits & 7));
do
{
nbits--;
assignbit(buff, bit + nbits, getbit(obj, bit + nbits));
} while (nbits);
return buff;
}
I need to create a memory struct for a project in which each word comprises 15 bits. When I check the size of the array I get that it is 2000 bytes in size, I assume it is because compiler byte alignment.
Is there a way to create the struct that it will be 1875 bytes in size?
This is the code I used:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#pragma pack(push,1)
struct myword
{
unsigned int val : 15;
};
struct mymemmory{
struct myword arr[1000];
};
#pragma pack(pop)
int main()
{
int size = sizeof(struct mymemmory);
printf("Size of arr: %d\n",size);
return 0;
}
When I use #pragma pack(push,0) I get that the size is 4000 bytes.
No, there isn't. If you need bit level granularity, you have to implement it yourself, making an array of the 1875 bytes and manually calculating indices and bit masks to extract the desired 15 bits. If you want to stay sane, you factor out this functionality into accessor functions, ideally using C++ or the like where you can make custom classes that abstract away the work entirely (so simple indexing use does all the "real index" and bit shift/mask work behind the scenes; std::vector<bool> already does something quite like this for single bits).
Of course, true sanity is realizing it's silly to quibble over 125 bytes. There are very few scenarios where saving one bit in sixteen for each value (especially for so few values) is worth it, and the ones I can think of (actually needing a compact representation on disk) are still handled better by converting from compact disk representation to expanded memory representation on read, and converted back on write, to avoid the hassle and computational overhead of dealing with all the shifting and masking on every read/write.
Unless you have a machine with 15-bit chars, you'll need to do a lot of bit manipulation to spread your 15-bit values over up to three unsigned chars using shifts and bit masks.
The following code works for machines with CHAR_BIT between 8 and 15 inclusive.
set15_le(mem, index, val) has mem pointing to an array of unsigned char providing the storage for an emulated array 15-bit words, index is the index of a 15-bit word, and val is a 15-bit value to be stored. get15_le(mem, index) returns the 15-bit word from the specified index. The 15-bit words are stored in "little-endian" byte order.
set15_be(mem, index, val) and get15_be(mem, index) are similar to the above, except that the 15-bit words are stored in "big-endian" byte order.
The main function tests both flavors, by storing a set of 15-bit, pseudo-random numbers in the array, reading them back, and checking they match the expected values.
#include <limits.h>
#if CHAR_BIT > 15
#error "Unsupported CHAR_BIT value"
#endif
unsigned short get15_le(const unsigned char *mem, unsigned long index)
{
unsigned long mem_index;
unsigned int mem_bitpos;
unsigned int val_bitpos;
unsigned short val_mask;
unsigned short val;
mem_index = (index * 15) / CHAR_BIT;
mem_bitpos = (index * 15) % CHAR_BIT;
val = 0;
val_bitpos = 0;
val_mask = (1U << 15) - 1;
while (val_mask)
{
unsigned int nbits;
unsigned char mem_mask;
unsigned char mem_byte;
nbits = CHAR_BIT - mem_bitpos;
if (nbits > 15 - val_bitpos)
{
nbits = 15 - val_bitpos;
}
mem_mask = val_mask << mem_bitpos;
mem_byte = mem[mem_index];
mem_byte &= mem_mask;
val |= (mem_byte >> mem_bitpos) << val_bitpos;
mem_bitpos += nbits;
if (mem_bitpos == CHAR_BIT)
{
mem_bitpos = 0;
mem_index++;
}
val_bitpos += nbits;
val_mask >>= nbits;
}
return val;
}
void set15_le(unsigned char *mem, unsigned long index, unsigned short val)
{
unsigned long mem_index;
unsigned int mem_bitpos;
unsigned int val_bitpos;
unsigned short val_mask;
mem_index = (index * 15) / CHAR_BIT;
mem_bitpos = (index * 15) % CHAR_BIT;
val_bitpos = 0;
val_mask = (1U << 15) - 1;
val &= val_mask;
while (val_mask)
{
unsigned int nbits;
unsigned char mem_mask;
unsigned char mem_byte;
nbits = CHAR_BIT - mem_bitpos;
if (nbits > 15 - val_bitpos)
{
nbits = 15 - val_bitpos;
}
mem_mask = val_mask << mem_bitpos;
mem_byte = mem[mem_index];
mem_byte &= ~mem_mask;
mem_byte |= ((val >> val_bitpos) << mem_bitpos) & mem_mask;
mem[mem_index] = mem_byte;
mem_bitpos += nbits;
if (mem_bitpos == CHAR_BIT)
{
mem_bitpos = 0;
mem_index++;
}
val_bitpos += nbits;
val_mask >>= nbits;
}
}
unsigned short get15_be(const unsigned char *mem, unsigned long index)
{
unsigned long mem_index;
unsigned int mem_bitpos;
unsigned int val_bitpos;
unsigned short val_mask;
unsigned short val;
mem_index = (index * 15) / CHAR_BIT;
mem_bitpos = CHAR_BIT - (index * 15) % CHAR_BIT;
val = 0;
val_bitpos = 15;
val_mask = (1U << 15) - 1;
while (val_mask)
{
unsigned int nbits;
unsigned char mem_mask;
unsigned char mem_byte;
nbits = mem_bitpos;
if (nbits > val_bitpos)
{
nbits = val_bitpos;
}
val_bitpos -= nbits;
mem_bitpos -= nbits;
mem_mask = (val_mask >> val_bitpos) << mem_bitpos;
mem_byte = mem[mem_index];
mem_byte &= mem_mask;
val |= (mem_byte >> mem_bitpos) << val_bitpos;
if (mem_bitpos == 0)
{
mem_bitpos = CHAR_BIT;
mem_index++;
}
val_mask >>= nbits;
}
return val;
}
void set15_be(unsigned char *mem, unsigned long index, unsigned short val)
{
unsigned long mem_index;
unsigned int mem_bitpos;
unsigned int val_bitpos;
unsigned short val_mask;
mem_index = (index * 15) / CHAR_BIT;
mem_bitpos = CHAR_BIT - (index * 15) % CHAR_BIT;
val_bitpos = 15;
val_mask = (1U << 15) - 1;
val &= val_mask;
while (val_mask)
{
unsigned int nbits;
unsigned char mem_mask;
unsigned char mem_byte;
nbits = mem_bitpos;
if (nbits > val_bitpos)
{
nbits = val_bitpos;
}
val_bitpos -= nbits;
mem_bitpos -= nbits;
mem_mask = (val_mask >> val_bitpos) << mem_bitpos;
mem_byte = mem[mem_index];
mem_byte &= ~mem_mask;
mem_byte |= ((val >> val_bitpos) << mem_bitpos) & mem_mask;
mem[mem_index] = mem_byte;
if (mem_bitpos == 0)
{
mem_bitpos = CHAR_BIT;
mem_index++;
}
val_mask >>= nbits;
}
}
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct mymemory
{
unsigned char content[(1000 * 15 + CHAR_BIT - 1) / CHAR_BIT];
};
int main(void)
{
struct mymemory mem;
unsigned long i;
unsigned short v;
printf("Memory size for 1000 15-bit words = %lu bytes (%lu bits)\n",
(unsigned long)sizeof(mem.content),
(unsigned long)sizeof(mem.content) * CHAR_BIT);
printf("Testing little-endian version\n");
memset(mem.content, 42, sizeof(mem.content));
srand(5);
for (i = 0; i < 1000; i++)
{
v = rand() & ((1U << 15) - 1);
set15_le(mem.content, i, v);
}
srand(5);
for (i = 0; i < 1000; i++)
{
unsigned int w;
v = rand() & ((1U << 15) - 1);
if ((w = get15_le(mem.content, i)) != v)
{
printf("Error at word %lu! got %u, expected %u\n", i, w, v);
break;
}
}
if (i == 1000)
{
printf("Passed!\n");
}
printf("Testing big-endian version\n");
memset(mem.content, 42, sizeof(mem.content));
srand(23);
for (i = 0; i < 1000; i++)
{
v = rand() & ((1U << 15) - 1);
set15_be(mem.content, i, v);
}
srand(23);
for (i = 0; i < 1000; i++)
{
unsigned int w;
v = rand() & ((1U << 15) - 1);
if ((w = get15_be(mem.content, i)) != v)
{
printf("Error at word %lu! got %u, expected %u\n", i, w, v);
break;
}
}
if (i == 1000)
{
printf("Passed!\n");
}
return 0;
}