Tagged Pointer
Copilot explanation:
The provided code defines two template classes, TaggedPtr2Bit
and TaggedPtr3Bit
, which are used to implement tagged pointers. A tagged pointer is a technique where you store additional data in the lower bits of a pointer variable. This is possible because on most systems, pointers to data types are aligned, meaning their addresses are multiples of the size of the type they point to. This leaves the least significant bits of the pointer as zero, which can be used to store additional information.
The TaggedPtr2Bit
class is designed for a 32-bit system, where pointers are 4-byte aligned. This means the 2 least significant bits of the pointer are always zero and can be used to store 2 boolean values. The class contains a pointer (ptr
) and two boolean values (b1
and b2
). The ptr
is stored as a uintptr_t
type, which is an integer type capable of holding a pointer. The :30
after ptr
in the private section of the class declaration is a bit-field that specifies that only the 30 most significant bits of ptr
are used to store the pointer value. The remaining 2 bits are used to store the boolean values.
The TaggedPtr3Bit
class is similar but is designed for a 64-bit system, where pointers are 8-byte aligned. This means the 3 least significant bits of the pointer are always zero and can be used to store 3 boolean values. The class contains a pointer (ptr
) and three boolean values (b1
, b2
, and b3
). The ptr
is stored as a uintptr_t
type, with a bit-field of :61
to specify that only the 61 most significant bits of ptr
are used to store the pointer value. The remaining 3 bits are used to store the boolean values.
Both classes provide methods to set and get the boolean values (setBool1
, setBool2
, setBool3
, getBool1
, getBool2
, getBool3
), and a type conversion operator to convert the object back to a pointer of the template type T
.
The commented-out section at the end of the code shows an example of how you might derive a class from TaggedPtr2Bit
to give the boolean values more meaningful names. In this case, the boolean value b1
is given the name Edit
.
Header:
/*
* Tagged pointer,
* 32 Bit: use the 2 least significant bits for 2 bool values. Use this when target is 4 Byte aligned.
* 64 Bit: use the 3 least significant bits for 3 bool values. Use this when target is 8 Byte aligned.
*/
#ifdef _WIN32
// 32 Bit - 4 Byte aligned
template<class T>
class TaggedPtr2Bit {
public:
inline TaggedPtr2Bit() : ptr(0), b1(0), b2(0)
{
}
inline explicit TaggedPtr2Bit(T* in) : ptr((uintptr_t)in), b1(0),b2(0)
{
// printf("4 Byte-PTR: %d\n", sizeof(T*));
}
inline operator T* () const {
return (T*)(uintptr_t)ptr;
/*
mov eax,dword ptr [this]
mov eax,dword ptr [eax]
and eax,3FFFFFFFh
*/
}
inline void setBool1(bool b) {
b1 = b;
}
inline void setBool2(bool b) {
b2 = b;
}
inline bool getBool1() {
return b1;
}
inline bool getBool2() {
return b2;
}
inline ~TaggedPtr2Bit()
{
ptr = 0;
b1 = 0;
b2 = 0;
}
private:
uintptr_t ptr : 30;
uintptr_t b1 : 1;
uintptr_t b2 : 1;
};
#endif
#if defined(_WIN64) || defined(_LINUX)
// 64 Bit - 8 Byte aligned
template<class T>
class TaggedPtr3Bit {
public:
inline TaggedPtr3Bit() : ptr(0), b1(0), b2(0), b3(0) {}
inline TaggedPtr3Bit(T* in) : ptr((uintptr_t)in), b1(0), b2(0), b3(0)
{
// printf("8 Byte-PTR: %d\n", sizeof(T*));
}
inline operator T* () const {
return (T*)(uintptr_t)ptr;
}
inline ~TaggedPtr3Bit()
{
ptr = 0;
b1 = 0;
b2 = 0;
b3 = 0;
}
inline void setBool1(bool b) { b1 = b; }
inline void setBool2(bool b) { b2 = b; }
inline void setBool3(bool b) { b3 = b; }
inline bool getBool1() { return b1; }
inline bool getBool2() { return b2; }
inline bool getBool3() { return b3; }
private:
uintptr_t ptr : 61;
uintptr_t b1 : 1;
uintptr_t b2 : 1;
uintptr_t b3 : 1;
};
#endif
/* derive and use better names
#include <bitset>
#include <iostream>
#include <cassert>
template<class T>
class TaggedPtrEdit :TaggedPtr2Bit<T> {
public:
inline TaggedPtrEdit() : TaggedPtr2Bit<T>() {
}
inline TaggedPtrEdit(T* in) : TaggedPtr2Bit<T>(in) {
assert((((uintptr_t)in & 3) == 0) && "Not 4 Byte aligned");
#ifdef DEBUG
std::bitset<32> bits((uintptr_t)in);
std::cout << in << " " << bits << '\n';
#endif
}
inline operator T* () const {
return TaggedPtr2Bit<T>::operator T * ();
}
inline void setEdit(bool b) {
TaggedPtr2Bit<T>::setBool1(b);
}
inline bool getEdit() {
return TaggedPtr2Bit<T>::getBool1();
}
};
*/
Test:
#include <iostream>
#include <cstdint>
int main( ) {
// Create an integer and a pointer to it
int x = 10;
int* p = &x;
// Create a TaggedPtr3Bit object with the pointer
TaggedPtr3Bit<int> taggedPtr(p);
// copy
TaggedPtr3Bit<int> copy(taggedPtr);
// Set the boolean values
copy.setBool1(true);
copy.setBool2(false);
// Get the boolean values
bool b1 = copy.getBool1(); // true
bool b2 = copy.getBool2(); // false
// Use the TaggedPtr3Bit object as a pointer
int* p2 = copy;
*p2 = 20; // Changes the value of x to 20
std::cout << x << " b1:" << b1 << " b2:" << b2 << std::endl;
return 0;
}