Integer to IP Address - C - c

I'm preparing for a quiz, and I have a strong suspicion I may be tasked with implementing such a function. Basically, given an IP address in network notation, how can we get that from a 32 bit integer into a string in it's dotted decimal notation (something like 155.247.182.83)...? Obviously we can't be using any type of inet functions either...I'm stumped!

You actually can use an inet function. Observe.
main.c:
#include <arpa/inet.h>
main() {
uint32_t ip = 2110443574;
struct in_addr ip_addr;
ip_addr.s_addr = ip;
printf("The IP address is %s\n", inet_ntoa(ip_addr));
}
The results of gcc main.c -ansi; ./a.out is
The IP address is 54.208.202.125
Note that a commenter said this does not work on Windows.

Here's a simple method to do it: The (ip >> 8), (ip >> 16) and (ip >> 24) moves the 2nd, 3rd and 4th bytes into the lower order byte, while the & 0xFF isolates the least significant byte at each step.
void print_ip(unsigned int ip)
{
unsigned char bytes[4];
bytes[0] = ip & 0xFF;
bytes[1] = (ip >> 8) & 0xFF;
bytes[2] = (ip >> 16) & 0xFF;
bytes[3] = (ip >> 24) & 0xFF;
printf("%d.%d.%d.%d\n", bytes[3], bytes[2], bytes[1], bytes[0]);
}
There is an implied bytes[0] = (ip >> 0) & 0xFF; at the first step.
Use snprintf() to print it to a string.

Another approach:
union IP {
unsigned int ip;
struct {
unsigned char d;
unsigned char c;
unsigned char b;
unsigned char a;
} ip2;
};
...
char ips[20];
IP ip;
ip.ip = 0xAABBCCDD;
sprintf(ips, "%x.%x.%x.%x", ip.ip2.a, ip.ip2.b, ip.ip2.c, ip.ip2.d);
printf("%s\n", ips);

Hint: break up the 32-bit integer to 4 8-bit integers, and print them out.
Something along the lines of this (not compiled, YMMV):
int i = 0xDEADBEEF; // some 32-bit integer
printf("%i.%i.%i.%i",
(i >> 24) & 0xFF,
(i >> 16) & 0xFF,
(i >> 8) & 0xFF,
i & 0xFF);

This is what I would do if passed a string buffer to fill and I knew the buffer was big enough (ie at least 16 characters long):
sprintf(buffer, "%d.%d.%d.%d",
(ip >> 24) & 0xFF,
(ip >> 16) & 0xFF,
(ip >> 8) & 0xFF,
(ip ) & 0xFF);
This would be slightly faster than creating a byte array first, and I think it is more readable. I would normally use snprintf, but IP addresses can't be more than 16 characters long including the terminating null.
Alternatively if I was asked for a function returning a char*:
char* IPAddressToString(int ip)
{
char[] result = new char[16];
sprintf(result, "%d.%d.%d.%d",
(ip >> 24) & 0xFF,
(ip >> 16) & 0xFF,
(ip >> 8) & 0xFF,
(ip ) & 0xFF);
return result;
}

#include "stdio.h"
void print_ip(int ip) {
unsigned char bytes[4];
int i;
for(i=0; i<4; i++) {
bytes[i] = (ip >> i*8) & 0xFF;
}
printf("%d.%d.%d.%d\n", bytes[3], bytes[2], bytes[1], bytes[0]);
}
int main() {
int ip = 0xDEADBEEF;
print_ip(ip);
}

From string to int and back
const char * s_ip = "192.168.0.5";
unsigned int ip;
unsigned char * c_ip = (unsigned char *)&ip;
sscanf(s_ip, "%hhu.%hhu.%hhu.%hhu", &c_ip[3], &c_ip[2], &c_ip[1], &c_ip[0]);
printf("%u.%u.%u.%u", ((ip & 0xff000000) >> 24), ((ip & 0x00ff0000) >> 16), ((ip & 0x0000ff00) >> 8), (ip & 0x000000ff));
%hhu instructs sscanf to read into unsigned char pointer; (Reading small int with scanf)
inet_ntoa from glibc
char *
inet_ntoa (struct in_addr in)
{
unsigned char *bytes = (unsigned char *) &in;
__snprintf (buffer, sizeof (buffer), "%d.%d.%d.%d",
bytes[0], bytes[1], bytes[2], bytes[3]);
return buffer;
}

My alternative solution with subtraction :)
void convert( unsigned int addr )
{
unsigned int num[OCTET],
next_addr[OCTET];
int bits = 8;
unsigned int shift_bits;
int i;
next_addr[0] = addr;
shift_bits -= bits;
num[0] = next_addr[0] >> shift_bits;
for ( i = 0; i < OCTET-1; i ++ )
{
next_addr[i + 1] = next_addr[i] - ( num[i] << shift_bits ); // next subaddr
shift_bits -= bits; // next shift
num[i + 1] = next_addr[i + 1] >> shift_bits; // octet
}
printf( "%d.%d.%d.%d\n", num[0], num[1], num[2], num[3] );
}

void ul2chardec(char*pcIP, unsigned long ulIPN){
int i; int k=0; char c0, c1;
for (i = 0; i<4; i++){
c0 = ((((ulIPN & (0xff << ((3 - i) * 8))) >> ((3 - i) * 8))) / 100) + 0x30;
if (c0 != '0'){ *(pcIP + k) = c0; k++; }
c1 = (((((ulIPN & (0xff << ((3 - i) * 8))) >> ((3 - i) * 8))) % 100) / 10) + 0x30;
if (!(c1 =='0' && c0=='0')){ *(pcIP + k) = c1; k++; }
*(pcIP +k) = (((((ulIPN & (0xff << ((3 - i) * 8)))) >> ((3 - i) * 8))) % 10) + 0x30;
k++;
if (i<3){ *(pcIP + k) = '.'; k++;}
}
*(pcIP + k) = 0; // pcIP should be x10 bytes
}

Related

memset char * with Integer

I have to memset 4 bytes in a char * with an integer.
For exemple I have an integer int i = 3276854 (0x00320036) and a char header[54] = {0}. I have to write i at header + 2, as if I print memory from header[2] to header[6], I get :
0032 0036
I tried this:
memset(&header[2], i, 1);
But it seems to put only the last byte into header, I get :
0036 0000
I also tried:
memset(&header[2], i, 4);
But it fill each byte with the last byte of i, I get :
3636 3636
I also tried to use binary masks like that :
ft_memset(&header[2], (int)(54 + size) & 0xff000000, 1);
ft_memset(&header[3], (int)(54 + size) & 0x00ff0000, 1);
ft_memset(&header[4], (int)(54 + size) & 0x0000ff00, 1);
ft_memset(&header[5], (int)(54 + size) & 0x000000ff, 1);
I get :
3600 0000.
So I don't know how I can get my 0032 0036, or at least 3600 3200 (maybe there is a thing with little and big endian into that, because I run it under MacOS, which is big endian).
memset fills memory with a constant byte value. The second parameter (of type int) is converted to an unsigned char value.
You could use memcpy like this:
memcpy(&header[2], &i, sizeof(i));
However, it depends what exactly you are trying to achieve. If the header needs the integer to be in a particular format, you may need to convert the value in some way. For example, if the value needs to be big-endian (which is also known as "network byte order" in several Internet protocols), you can convert it with the htonl function:
uint32_t bi = htonl(i);
memcpy(&header[2], &bi, sizeof(bi));
(The htonl function is defined by #include <arpa/inet.h>.)
Also check the newer byte order conversion functions htobe16, htole16, be16toh, le16toh, htobe32, htole32, be32toh, le32toh, htobe64, htole64, be64toh, and le64toh declared by:
#define _BSD_SOURCE
#include <endian.h>
These convert between host byte order and little-endian byte order, or between host byte order and big-endian byte order, and work on uint16_t, uint32_t or uint64_t values, depending on the function name.
If there are no equivalents to those byte-order conversion functions provided on your system the following non-optimized, but portable (on implementations that support uint16_t, uint32_t and uint64_t) functions may be used:
myendian.h
#ifndef MYENDIAN_H__INCLUDED_
#define MYENDIAN_H__INCLUDED_
#include <stdint.h>
uint16_t my_htobe16(uint16_t h16);
uint16_t my_htole16(uint16_t h16);
uint16_t my_be16toh(uint16_t be16);
uint16_t my_le16toh(uint16_t le16);
uint32_t my_htobe32(uint32_t h32);
uint32_t my_htole32(uint32_t h32);
uint32_t my_be32toh(uint32_t be32);
uint32_t my_le32toh(uint32_t le32);
uint64_t my_htobe64(uint64_t h64);
uint64_t my_htole64(uint64_t h64);
uint64_t my_be64toh(uint64_t be64);
uint64_t my_le64toh(uint64_t le64);
#endif
myendian.c
#include "myendian.h"
union swab16
{
uint16_t v;
uint8_t b[2];
};
union swab32
{
uint32_t v;
uint8_t b[4];
};
union swab64
{
uint64_t v;
uint8_t b[8];
};
static uint16_t xbe16(uint16_t x)
{
union swab16 s;
s.b[0] = (x >> 8) & 0xffu;
s.b[1] = x & 0xffu;
return s.v;
}
static uint16_t xle16(uint16_t x)
{
union swab16 s;
s.b[0] = x & 0xffu;
s.b[1] = (x >> 8) & 0xffu;
return s.v;
}
static uint32_t xbe32(uint32_t x)
{
union swab32 s;
s.b[0] = (x >> 24) & 0xffu;
s.b[1] = (x >> 16) & 0xffu;
s.b[2] = (x >> 8) & 0xffu;
s.b[3] = x & 0xffu;
return s.v;
}
static uint32_t xle32(uint32_t x)
{
union swab32 s;
s.b[0] = x & 0xffu;
s.b[1] = (x >> 8) & 0xffu;
s.b[2] = (x >> 16) & 0xffu;
s.b[3] = (x >> 24) & 0xffu;
return s.v;
}
static uint64_t xbe64(uint64_t x)
{
union swab64 s;
s.b[0] = (x >> 56) & 0xffu;
s.b[1] = (x >> 48) & 0xffu;
s.b[2] = (x >> 40) & 0xffu;
s.b[3] = (x >> 32) & 0xffu;
s.b[4] = (x >> 24) & 0xffu;
s.b[5] = (x >> 16) & 0xffu;
s.b[6] = (x >> 8) & 0xffu;
s.b[7] = x & 0xffu;
return s.v;
}
static uint64_t xle64(uint64_t x)
{
union swab64 s;
s.b[0] = x & 0xffu;
s.b[1] = (x >> 8) & 0xffu;
s.b[2] = (x >> 16) & 0xffu;
s.b[3] = (x >> 24) & 0xffu;
s.b[4] = (x >> 32) & 0xffu;
s.b[5] = (x >> 40) & 0xffu;
s.b[6] = (x >> 48) & 0xffu;
s.b[7] = (x >> 56) & 0xffu;
return s.v;
}
uint16_t my_htobe16(uint16_t h16)
{
return xbe16(h16);
}
uint16_t my_htole16(uint16_t h16)
{
return xle16(h16);
}
uint16_t my_be16toh(uint16_t be16)
{
return xbe16(be16);
}
uint16_t my_le16toh(uint16_t le16)
{
return xle16(le16);
}
uint32_t my_htobe32(uint32_t h32)
{
return xbe32(h32);
}
uint32_t my_htole32(uint32_t h32)
{
return xle32(h32);
}
uint32_t my_be32toh(uint32_t be32)
{
return xbe32(be32);
}
uint32_t my_le32toh(uint32_t le32)
{
return xle32(le32);
}
uint64_t my_htobe64(uint64_t h64)
{
return xbe64(h64);
}
uint64_t my_htole64(uint64_t h64)
{
return xle64(h64);
}
uint64_t my_be64toh(uint64_t be64)
{
return xbe64(be64);
}
uint64_t my_le64toh(uint64_t le64)
{
return xle64(le64);
}
Test harness: myendiantest.c
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include "myendian.h"
#define TEST(n, fn, v) \
printf("%s(%#" PRIx##n ") = %#" PRIx##n "\n", #fn, (v), (fn)(v))
int main(void)
{
const uint16_t t16 = UINT16_C(0x1234);
const uint32_t t32 = UINT32_C(0x12345678);
const uint64_t t64 = UINT64_C(0x123456789abcdef);
TEST(16, my_htobe16, t16);
TEST(16, my_htole16, t16);
TEST(16, my_be16toh, t16);
TEST(16, my_le16toh, t16);
TEST(32, my_htobe32, t32);
TEST(32, my_htole32, t32);
TEST(32, my_be32toh, t32);
TEST(32, my_le32toh, t32);
TEST(64, my_htobe64, t64);
TEST(64, my_htole64, t64);
TEST(64, my_be64toh, t64);
TEST(64, my_le64toh, t64);
return 0;
}
Output on a little endian system:
my_htobe16(0x1234) = 0x3412
my_htole16(0x1234) = 0x1234
my_be16toh(0x1234) = 0x3412
my_le16toh(0x1234) = 0x1234
my_htobe32(0x12345678) = 0x78563412
my_htole32(0x12345678) = 0x12345678
my_be32toh(0x12345678) = 0x78563412
my_le32toh(0x12345678) = 0x12345678
my_htobe64(0x123456789abcdef) = 0xefcdab8967452301
my_htole64(0x123456789abcdef) = 0x123456789abcdef
my_be64toh(0x123456789abcdef) = 0xefcdab8967452301
my_le64toh(0x123456789abcdef) = 0x123456789abcdef
Output on a big endian system (expected, but not tested by me):
my_htobe16(0x1234) = 0x1234
my_htole16(0x1234) = 0x3412
my_be16toh(0x1234) = 0x1234
my_le16toh(0x1234) = 0x3412
my_htobe32(0x12345678) = 0x12345678
my_htole32(0x12345678) = 0x78563412
my_be32toh(0x12345678) = 0x12345678
my_le32toh(0x12345678) = 0x78563412
my_htobe64(0x123456789abcdef) = 0x123456789abcdef
my_htole64(0x123456789abcdef) = 0xefcdab8967452301
my_be64toh(0x123456789abcdef) = 0x123456789abcdef
my_le64toh(0x123456789abcdef) = 0xefcdab8967452301
You could use memcpy but that would make your code dependent on the underlying endianess of the CPU. I don't think that's what you want.
My take is that you want to convert to the network endianess of whatever data communications protocol this is, regardless of CPU endianess.
The only way to achieve that is bit shifts. The byte order depends on the target network endianess:
void u32_to_big_endian (uint8_t* dst, const uint32_t src)
{
dst[0] = (uint8_t) ((src >> 24) & 0xFFu);
dst[1] = (uint8_t) ((src >> 16) & 0xFFu);
dst[2] = (uint8_t) ((src >> 8) & 0xFFu);
dst[3] = (uint8_t) ((src >> 0) & 0xFFu);
}
void u32_to_little_endian (uint8_t* dst, const uint32_t src)
{
dst[3] = (uint8_t) ((src >> 24) & 0xFFu);
dst[2] = (uint8_t) ((src >> 16) & 0xFFu);
dst[1] = (uint8_t) ((src >> 8) & 0xFFu);
dst[0] = (uint8_t) ((src >> 0) & 0xFFu);
}
In these functions, it doesn't matter what the CPU endianess is. You can forget about memcpy() and non-standard endianess conversion functions. Full example:
#include <stdio.h>
#include <stdint.h>
void u32_to_big_endian (uint8_t* dst, const uint32_t src)
{
dst[0] = (uint8_t) ((src >> 24) & 0xFFu);
dst[1] = (uint8_t) ((src >> 16) & 0xFFu);
dst[2] = (uint8_t) ((src >> 8) & 0xFFu);
dst[3] = (uint8_t) ((src >> 0) & 0xFFu);
}
void u32_to_little_endian (uint8_t* dst, const uint32_t src)
{
dst[3] = (uint8_t) ((src >> 24) & 0xFFu);
dst[2] = (uint8_t) ((src >> 16) & 0xFFu);
dst[1] = (uint8_t) ((src >> 8) & 0xFFu);
dst[0] = (uint8_t) ((src >> 0) & 0xFFu);
}
int main(void)
{
uint32_t i = 0x00320036u;
uint8_t header[54] = {0};
u32_to_little_endian(&header[2], i);
for(size_t i=0; i<6; i++)
{
printf("%.2x ", (unsigned int)header[i]);
}
}
Output, including the first 2 bytes as zeroes:
00 00 36 00 32 00

Circular shift 28 bits within 4 bytes in C

I have an unsigned char *Buffer that contains 4 bytes, but only 28 of them are relevant to me.
I am looking to create a function that will do a circular shift of the 28 bits while ignoring the remaining 4 bits.
For example, I have the following within *Buffer
1111000011001100101010100000
Say I want to left circular shift by 1 bit of the 28 bits, making it
1110000110011001010101010000
I have looked around and I can't figure out how to get the shift, ignore the last 4 bits, and have the ability to shift either 1, 2, 3, or 4 bits depending on a variable set earlier in the program.
Any help with this would be smashing! Thanks in advance.
Only 1 bit at a time, but this does a 28 bit circular shift
uint32_t csl28(uint32_t value) {
uint32_t overflow_mask = 0x08000000;
uint32_t value_mask = 0x07FFFFFF;
return ((value & value_mask) << 1) | ((value & overflow_mask) >> 27);
}
uint32_t csr28(uint32_t value) {
uint32_t overflow_mask = 0x00000001;
uint32_t value_mask = 0x0FFFFFFE;
return ((value & value_mask) >> 1) | ((value & overflow_mask) << 27);
}
Another version, based on this article. This shifts an artbitrary number of bits (count) within an arbitrarily wide bit field (width). To left shift a value 5 bits in a 23 bit wide field: rotl32(value, 5, 23);
uint32_t rotl32 (uint32_t value, uint32_t count, uint32_t width) {
uint32_t value_mask = ((uint32_t)~0) >> (CHAR_BIT * sizeof(value) - width);
const uint32_t mask = (width-1);
count &= mask;
return value_mask & ((value<<count) | (value>>( (-count) & mask )));
}
uint32_t rotr32 (uint32_t value, uint32_t count, uint32_t width) {
uint32_t value_mask = ((uint32_t)~0) >> (CHAR_BIT * sizeof(value) - width);
const uint32_t mask = (width-1);
count &= mask;
return value_mask & ((value>>count) | (value<<( (-count) & mask )));
}
The above functions assume the value is stored in the low order bits of "value"
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
const char *uint32_to_binary(uint32_t x)
{
static char b[33];
b[0] = '\0';
uint32_t z;
for (z = 0x80000000; z > 0; z >>= 1)
{
strcat(b, ((x & z) == z) ? "1" : "0");
}
return b;
}
uint32_t reverse(uint32_t value)
{
return (value & 0x000000FF) << 24 | (value & 0x0000FF00) << 8 |
(value & 0x00FF0000) >> 8 | (value & 0xFF000000) >> 24;
}
int is_big_endian(void)
{
union {
uint32_t i;
char c[4];
} bint = {0x01020304};
return bint.c[0] == 1;
}
int main(int argc, char** argv) {
char b[] = { 0x98, 0x02, 0xCA, 0xF0 };
char *buffer = b;
//uint32_t num = 0x01234567;
uint32_t num = *((uint32_t *)buffer);
if (!is_big_endian()) {
num = reverse(*((uint32_t *)buffer));
}
num >>= 4;
printf("%x\n", num);
for(int i=0;i<5;i++) {
printf("%s\n", uint32_to_binary(num));
num = rotl32(num, 3, 28);
}
for(int i=0;i<5;i++) {
//printf("%08x\n", num);
printf("%s\n", uint32_to_binary(num));
num = rotr32(num, 3, 28);
}
unsigned char out[4];
memset(out, 0, sizeof(unsigned char) * 4);
num <<= 4;
if (!is_big_endian()) {
num = reverse(num);
}
*((uint32_t*)out) = num;
printf("[ ");
for (int i=0;i<4;i++) {
printf("%s0x%02x", i?", ":"", out[i] );
}
printf(" ]\n");
}
First you mask the top four most significant bits
*(buffer + 3) &= 0x0F;
Then you can perform the circular shift of the remaining 28 bits by x bits.
Note: This will work for little endian architecture(x86 Pc's and most microcontrollers)
[...] that contains 4 bytes, but only 28 of them [...]
We got it, but...
I guess that you mis-typed the second number of your example. Or you '''ignore''' 4 bits from left and right so you're actually interrested in 24 bits? Anyway:
Use same principle as in
Circular shift in c.
You need to convert your Buffer to a 32 bit arithmetic type, before. Maybe uint32_t is what you need?
Where did Buffer get his value? You may need to think about endianness.

Converting number to byte array

Hi I have a base 10 number for example 3198, and the hex representation is 0x0C7E
How do I convert that number to hex and put that hex value in a byte array in the format of [00][0C][7E], assuming the biggest hex value i can have is 0xffffff.
Maybe this will work ?
uint32_t x = 0x0C7E;
uint8_t bytes[3];
bytes[0] = (x >> 0) & 0xFF;
bytes[1] = (x >> 8) & 0xFF;
bytes[2] = (x >> 16) & 0xFF;
/* Go back. */
x = (bytes[2] << 16) | (bytes[1] << 8) | (bytes[0] << 0);
Number is already a continuous memory block - no need to convert it to yet ANOTHER array ! Just fetch separate bytes by using pointer arithmetic:
EDIT: Edited to be endianness-independent
#define FAST_ABS(x) ((x ^ (x>>31)) - (x>>31))
int is_big_endian(void)
{
union {
uint32_t i;
char c[4];
} bint = {0x01020304};
return bint.c[0] == 1;
}
uint32_t num = 0xAABBCCDD;
uint32_t N = is_big_endian() * 3;
printf("first byte 0x%02X\n"
"second byte 0x%02X\n"
"third byte 0x%02X\n"
"fourth byte 0x%02X\n",
((unsigned char *) &num)[FAST_ABS(3 - N)],
((unsigned char *) &num)[FAST_ABS(2 - N)],
((unsigned char *) &num)[FAST_ABS(1 - N)],
((unsigned char *) &num)[FAST_ABS(0 - N)]
);
#include <stdio.h>
union uint32_value {
unsigned int value;
struct little_endian {
unsigned char fou;
unsigned char thi;
unsigned char sec;
unsigned char fir;
} le;
struct big_endian {
unsigned char fir;
unsigned char sec;
unsigned char thi;
unsigned char fou;
} be;
};
int main(void)
{
union uint32_value foo;
foo.value = 3198;
printf("%02x %02x %02x %02x\n", foo.le.fir, foo.le.sec, foo.le.thi, foo.le.fou);
return 0;
}

How to convert 64 bit int to binary presentation?

How to convert 64 bit int to binary presentation (big endian)? For reverse task I use these functions:
int readInt (struct str *buf) {
buf -> cur_len = buf -> cur_len + 4;
return
(((buf -> data[buf -> cur_len - 3 ] & 0xff) << 24) |
((buf -> data[buf -> cur_len - 2 ] & 0xff) << 16) |
((buf -> data[buf -> cur_len - 1 ] & 0xff) << 8) |
((buf -> data[buf -> cur_len ] & 0xff) << 0));
};
long unsigned int 32Bit(struct str *buf) { // 32
return ((long unsigned int)readInt(buf)) & 0xffffffffL;
};
long unsigned int 64Bit(struct str *buffer) { //64
long unsigned int result = 32Bit(buf);
result *= 4294967296.0;
return result;
}
Serialising a 64 bit unsigned number into an array of unsigned char, storing 8 bits in each in big-endian order, can be done like so:
void serialise_64bit(unsigned char dest[8], unsigned long long n)
{
dest[0] = (n >> 56) & 0xff;
dest[1] = (n >> 48) & 0xff;
dest[2] = (n >> 40) & 0xff;
dest[3] = (n >> 32) & 0xff;
dest[4] = (n >> 24) & 0xff;
dest[5] = (n >> 16) & 0xff;
dest[6] = (n >> 8) & 0xff;
dest[7] = (n >> 0) & 0xff;
}
You shouldn't use built-in types for serialization; instead, when you need to know the exact size of a type, you need fixed-width types:
#include <stdint.h>
unsigned char buf[8]; // 64-bit raw data
uint64_t little_endian_value =
(uint64_t)buf[0] + ((uint64_t)buf[1] << 8) + ((uint64_t)buf[2] << 16) + ... + ((uint64_t)buf[7] << 56);
uint64_t big_endian_value =
(uint64_t)buf[7] + ((uint64_t)buf[6] << 8) + ((uint64_t)buf[5] << 16) + ... + ((uint64_t)buf[0] << 56);
Similarly for 32-bit values, use uint32_t there. Make sure your source buffer uses unsigned chars.

How to circular shift an array of 4 chars?

I have an array of four unsigned chars. I want to treat it like a 32-bit number (assume the upper bits of the char are don't care. I only care about the lower 8-bits). Then, I want to circularly shift it by an arbitrary number of places. I've got a few different shift sizes, all determined at compile-time.
E.g.
unsigned char a[4] = {0x81, 0x1, 0x1, 0x2};
circular_left_shift(a, 1);
/* a is now { 0x2, 0x2, 0x2, 0x5 } */
Edit: To everyone wondering why I didn't mention CHAR_BIT != 8, because this is standard C. I didn't specify a platform, so why are you assuming one?
static void rotate_left(uint8_t *d, uint8_t *s, uint8_t bits)
{
const uint8_t octetshifts = bits / 8;
const uint8_t bitshift = bits % 8;
const uint8_t bitsleft = (8 - bitshift);
const uint8_t lm = (1 << bitshift) - 1;
const uint8_t um = ~lm;
int i;
for (i = 0; i < 4; i++)
{
d[(i + 4 - octetshifts) % 4] =
((s[i] << bitshift) & um) |
((s[(i + 1) % 4] >> bitsleft) & lm);
}
}
Obviously
While keeping in mind plain C the best way is
inline void circular_left_shift(char *chars, short shift) {
__int32 *dword = (__int32 *)chars;
*dword = (*dword << shift) | (*dword >> (32 - shift));
}
Uhmm, char is 16 bits long, was not clear for me. I presume int is still 32 bit.
inline void circular_left_shift(char *chars, short shift) {
int i, part;
part = chars[0] >> (16 - shift);
for (i = 0; i < 3; ++i)
chars[i] = (chars[i] << shift) | (chars[i + 1] >> (16 - shift));
chars[3] = (chars[3] << shift) | part;
}
Or you could just unwind this cycle.
You could dig further into asm instruction ror, on x86 it's capable of performing such shift up to 31 bits left. Something like a
MOV CL, 31
ROR EAX, CL
Use union:
typedef union chr_int{
unsigned int i;
unsigned char c[4];
};
It's safer (because of pointer aliasing) and easier to manipulate.
EDIT: you should have mention earlier that your char isn't 8 bits. However, this should do the trick:
#define ORIG_MASK 0x81010102
#define LS_CNT 1
unsigned char a[4] = {
((ORIG_MASK << LS_CNT ) | (ORIG_MASK >> (32 - LS_CNT))) & 0xff,
((ORIG_MASK << (LS_CNT + 8)) | (ORIG_MASK >> (24 - LS_CNT))) & 0xff,
((ORIG_MASK << LS_CNT + 16)) | (ORIG_MASK >> (16 - LS_CNT))) & 0xff,
((ORIG_MASK << (LS_CNT + 24)) | (ORIG_MASK >> ( 8 - LS_CNT))) & 0xff
};

Resources