Home > Software engineering >  Is it possible to set bits of a void* value without type casting it to an int or char?
Is it possible to set bits of a void* value without type casting it to an int or char?

Time:04-13

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));
}
  • Related