tags:

views:

141

answers:

4

Following function is used to get the page's base address of an address which is inside this page:

void* GetPageAddress(void* pAddress)
{
    return (void*)((ULONG_PTR)pAddress & ~(PAGE_SIZE - 1));
}

But I couldn't quite get it, what is the trick it plays here?

Conclusion:
Personally, I think Amardeep's explanation plus Alex B's example are best answers. As Alex B's answer has already been voted up, I would like to accept Amardeep's answer as the official one to highlight it! Thanks you all.

+6  A: 

What it does is clears the bits of the address that fit within the mask created by the page size. Effectively it gets the first valid address of a block.

PAGE_SIZE must be a power of 2 and is represented by a single bit set in the address.

The mask is created by subtracting one from PAGE_SIZE. That effectively sets all the bits that are a lower order than the page size bit. The ~ then complements all those bits to zero and sets all the bits that are a higher order than the mask. The & then effectively strips all the lower bits away, leaving the actual base address of the page containing the original address.

Amardeep
lz_prgmr
The ~ will return the same size as its operand. In this case, if PAGE_SIZE is just a macro defined to a literal number, it will default to whatever 'int' is on your target platform, probably 32 bits. The code example you gave will not work on a 64 bit system. It would need: return (void*)((ULONG_PTR)pAddress
Amardeep
+4  A: 

When PAGE_SIZE is some power of 2 (say 4096 for example), this clears all the bits below those that specify the page.

bbudge
Yea, seems the PAGE_SIZE has to be power of 2, or else the result is wrong, but then why ~(PAGE_SIZE-1), I think we could just use PAGE_SIZE
lz_prgmr
@Dbger: PAGE_SIZE, if a power of two is represented by a single bit. You need all the bits set that are lower order than that one in order to create a mask of the correct size.
Amardeep
If PAGE_SIZE is a power of 2, then complementing it will give you a mask with only a single zero bit, which doesn't help much.
TMN
Mentioning sign extension when the 32-bit value gets expanded to 64-bits might be necessary.
Hans Passant
+14  A: 

The function clears low bits of a given address, which yields the address of its page.

For example, if PAGE_SIZE is 4096, then in 32-bit binary:

   PAGE_SIZE      = 00000000000000000001000000000000b
   PAGE_SIZE - 1  = 00000000000000000000111111111111b
 ~(PAGE_SIZE - 1) = 11111111111111111111000000000000b

If you bitwise-and it with a 32-bit address, it will turn lower bits into zeros, rounding the address to the nearest 4096-byte page address.

 ~(PAGE_SIZE - 1)             = 11111111111111111111000000000000b
                    pAddress  = 11010010100101110110110100100100b
 ~(PAGE_SIZE - 1) & pAddress  = 11010010100101110110000000000000b

So, in decimal, original address is 3533139236, page address (address with lower bits stripped) is 3533135872 = 862582 x 4096, is a multiple of 4096.

Alex B
Nice work with the numeric example.
mskfisher
A: 

This is just a tricky way of clearing the low order bits.

Another way to implement it might be

void* GetPageAddress(void* pAddress)
{
    return pAddress - pAddress%PAGE_SIZE;
}

That probably won't compile, as you need to cast the types a bit, bit it shows the algorithm.

Effectively it is getting the largest multiple of PAGE_SIZE that is less than pAddress.

Michael J