Showing posts with label C. Show all posts
Showing posts with label C. Show all posts

Friday, August 30, 2013

Endianity conversion for a 24bit Integer

Recently while working on SSL protocol I came across 24-bit (3 byte) integers. I never thought any one would ever use a 24-bit integer. There are no default types or any type in stdint.h to create such a variable. So the best one could do is use 3 byte arrays and keep typecasting them to int - ignoring most significant byte on read and not writing the most significant byte on write.
The problem I faced was that I had to change the endianity of these 24-bit integers as the data is written to and read from network. I did not want to use a function call so I came up with following macros. They dont seem right to me i.e they are more weird looking and complicated than I wanted to so if someone has a simple solution, please share.

The following macro takes a pointer to 3 bytes containing 24 bit unsigned integer, converts the endianity of the integer value and returns it as int with most significant byte always 0
#define GET_NO_UINT24(src)  ((((*(uint32_t*)src)<<16)|((*(uint32_t*)src) & 0xFF00)|(((*(uint32_t*)src)>>16) & 0xFF)) & 0xFFFFFF)
The next macro takes 2 arguments. The first one is a pointer to 3 byte array for storing 24-bit integer and the second argument is the value to be stored.
#define PUT_NO_UINT24(dst,src) ((((unsigned char*)dst)[2]=(unsigned char)((src))), (((unsigned char*)dst)[1]=(unsigned char)((src)>>8)), (((unsigned char*)dst)[0]=(unsigned char)((src)>>16)))

Tuesday, February 21, 2012

C Language: Struct packing and member alignment.

I know this has been discussed and re-discussed at so many locations and so many times that me writing about it again wont make much of a difference but I still see so many people confused about this that I cant stop my self from writing about it.

By default C compilers properly align each member of struct. This means that a 2-byte member e.g. short is aligned on 2-byte boundary, a 4-byte member e.g. int is aligned on 4-byte boundary and so on. This is done so that the struct members can be accessed efficiently and to reduce cache misses. To ensure proper alignment compilers add padding bytes to structs. Consider for example the following struct:
struct SomeData
{
    char Data1;
    short Data2;
    int Data3;
    char Data4;
};

Now as you can can see we are only using 8 bytes for data, but the compiler will add padding bytes to this struct to ensure proper alignment of its member. So a variable of this struct type will actually be like this after compilation on a 32-Bit machine:
struct SomeData
{
    /* 1 byte */
    char Data1; 
    /* 1 byte so the following 'short' can be aligned on a 2 byte boundary*/
    char Padding1[1]; 
    /* 2 bytes */
    short Data2;
    /* 4 bytes - largest struct member */
    int Data3;
    /* 1 byte */
    char Data4;
    /* 3 bytes to make total size of the struct 12 bytes */
    char Padding2[3];
};

The compiled size of the structure is now 12 bytes. It is important to note that the last member is padded with the number of bytes required to make total size of the struct a multiple of the size of the largest member of a struct. In this case 3 bytes are added to the last member to pad the struct to the size of a 12 bytes (4 bytes of int × 3).

As you can see, we are wasting memory. We can off course stop compiler from doing this by asking compiler to pack structs tightly (using #pragma pack() with gcc) but then we lose performance benefits of proper alignment.

In order to avoid these padding bytes but still have proper alignment, we can rearrange this struct so that larger members are listed before the smaller ones. So the above struct can be rearranged as:
struct SomeData
{
    int Data3;
    short Data2;
    char Data1;
    char Data4;
};
Now this does not require any padding as every element is already properly aligned and the overall size of the struct is a multiple of the largest member i.e 4*2 = 8. This is off-course just an example and in reality, even if you arrange your struct members this way, there will still be some padding at the end of the struct to make total size of the struct a multiple of the size of the largest struct member
So please always arrange members of your structs in decreasing order of their size.

Monday, May 23, 2011

DON'T hide pointers

I have seen alot of code where typedefs are used to create pointer types like:

typedef unsigned char *Message;
Some people might think that this is a very good thing to do or that it serves some (obscure) purpose but in my opinion such typedefs are plain wrong and evil. They cause alot of errors. Why would a sane person want to hide the fact that a variable of type Message is actually a pointer? It serves no pupose, is usually longer to type and creates disparity between declaration and use of a variable which makes code harder to understand for a new commer and a nightmare to maintain.

Wednesday, January 19, 2011

Using LD, the GNU Linker, to create a relocatable section of code or data

Relocatable sections are used when you cant load a section of code/data directly where you want. This may be for many reasons for example the memory where the code is supposed to go needs to be initialized first as is the case in ROM images.

For a relocatable section you need two addresses:
1. Relocation Address
2. Load Address

Relocation Address is the address for which the section is linked. Whereas Load Address is the address where the section will get loaded. Confusing? Lets try an example:

Consider an embedded system with 64MB ROM memory mapped at address 0x1E000000 and 256 MB DRAM mapped at 0x00000000. Your code will reside in ROM but when the system starts, it will copy it self from ROM into DRAM and continue execution from there.

To create a relocatable section we use AT keyword in section definition as follows
section_name startaddr : AT( loadaddr)
{
    /*section contents*/
    ...
}
 This will create a section which is linked for address startaddr but which is loaded in memory at loadaddr. Going back to above example embedded system, you will need at least two sections, one will contain the actual code and the other will contain the relocation/copying logic. This is required because the code for copying will get executed from ROM, so it needs to be linked for that address. So the GNU ld script will look something like following:

.init 0x1E000000 :
{
    *(.init)
}
.text 0x00000000 : AT ( ADDR(.init) + SIZEOF(.init) )
{
    *(.text)
}
the expression ADDR(.init) + SIZEOF(.init) will give us the address of first byte just after .init section. So .text section will get loaded just after the .init section. Upon reset, the code in .init section will copy the text section to DRAM i.e address 0x00000000 and pass control to it.