I am working on a project that requires me to allocate some space using the mmap
function and I need to create a bitmap in the beginning of the allocated space. The bitmap shows which sections are occupied and which sections are free, sections being divided into 8 byte words. Each bit in the bitmap indicates whether its corresponding word is free or occupied. I am pointing to the space via a void*
but I can not individually set the bits without making a type cast into an int. I know that I can set the bits in bulks of 32 by setting each int to -1, since my compiler uses 2's compliment but different compilers have different implementations. It also has the problem of using unnecessary bits if the number of words is not a multiple of 32.
Is it possible to set those bits one by one without type casting them?
CodePudding user response:
Is it possible to set those bits one by one without type casting them?
Yes.
Let us take advantage of:
A pointer to
void
shall have the same representation and alignment requirements as a pointer to a character type. C17dr § 6.2.5 28
Given void *
and some bit index:
// No cast, access void * via union.
void set_bit_without_cast1(void *data, size_t bit_index) {
union {
void *vp;
unsigned char *ucp;
} u = { .vp = data };
u.ucp[bit_index / CHAR_BIT] |= 1u << (bit_index % CHAR_BIT);
}
Or by simple assignment, no cast needed from void *
to object pointer.
// No cast needed.
void set_bit_without_cast2(void *data, size_t bit_index) {
unsigned char *ucp = data;
ucp[bit_index / CHAR_BIT] |= 1u << (bit_index % CHAR_BIT);
}
OP has "I am pointing to the space via a void*
". Consider instead alternative code such so that "... pointing to the space via a unsigned char *
" to simplify even further.
CodePudding user response:
void*
are pointers to memory that can hold any data of any type that can fit in it. You don't want to leave your void*
uncast because you cannot dereference it until it is at least implicitly cast to a pointer to a concrete type.
To make a bitmap for a data field in one contiguous memory region, you want to create a structure with bitmap and data field members, and initialize a pointer to that structure with the return value of the mmap()
call.
Here is a short example:
#define CHUNK_SIZE 2048 //Pick whatever size you're mmaping (at least 9 bytes)
//If CHUNK_SIZE is divisible by 65 there's no wasted space otherwise there's
//somewhere between one bit and seven bytes seven bits wasted
//One whole byte of bitmap and the 8 8-byte words it can map take up 65 bytes
#define MAP_LEN (CHUNK_SIZE % 65 ? CHUNK_SIZE / 65 1 : CHUNK_SIZE / 65)
#define DATA_LEN ((CHUNK_SIZE-MAP_LEN)/8)
//This struct describes the layout of the whole mmap()ed region
struct chunk{
uint8_t bitmap[MAP_LEN]; //Bitmap
uint64_t data[DATA_LEN]; //8-byte words being mapped
};
int main(){
struct chunk *mem = mmap(NULL,CHUNK_SIZE,/*Whatever your other mmap parameters are*/);
memset(mem,0,CHUNK_SIZE); //This is only necessary if not mapped anonymously
//And only the bitmap strictly must be cleared
//Then store bytes like so
mem->data[5] = 0xFF; //#5<-256 (5 must be less than DATA_LEN)
mem->bitmap[5/8] |= 1<<(5%8); //Put a 1 in the proper bit in the bitmap
//And clear the bit when you're finished with its byte
mem->bitmap[5/8] &= ~(1<<(5%8));
}