Skip to content

Instantly share code, notes, and snippets.

@morrisonlevi
Last active August 29, 2015 14:27
Show Gist options
  • Save morrisonlevi/e16344d9f35c4910cbb5 to your computer and use it in GitHub Desktop.
Save morrisonlevi/e16344d9f35c4910cbb5 to your computer and use it in GitHub Desktop.
A discriminated or tagged pointer that uses variadic templates and takes only 48 bits of memory.
#ifndef SMALL_PTR_HH
#define SMALL_PTR_HH
#include <cassert>
#include <cstdint>
#include <cstring>
static_assert(sizeof(void *) == 8, "Pointers must be 8 bytes");
static_assert(sizeof(uintptr_t) == 8, "uintptr_t must be 8 bytes");
/* Assumptions:
* - All pointers are properly aligned such that they never use their lower 3 bits
* - No pointer has any bits set in the upper two bytes
* - NULL also abides by these rules
* - Each byte is 8 bits
* - Endianness
* - alignof(small_ptr) == 1 won't hurt anything
*/
template<typename... Types>
class small_ptr {
template<typename... Rest>
struct TypeIndex;
template<typename T, typename... Rest>
struct TypeIndex<T, T, Rest...> {
static constexpr uint8_t value = 1;
};
template<typename T, typename U, typename... Rest>
struct TypeIndex<T, U, Rest...> {
static constexpr uint8_t value = 1 + TypeIndex<T, Rest...>::value;
};
static constexpr uint8_t tag_mask = 0b0111;
static constexpr uintptr_t ptr_mask = ~uintptr_t(0b0111);
static_assert(sizeof...(Types) <= tag_mask, "Too many types for small_ptr");
uint8_t bytes[6];
uint8_t type() const {
return bytes[0] & tag_mask;
}
void * value() const {
uintptr_t ptr;
std::memcpy(&ptr, bytes, 6);
std::memset(reinterpret_cast<uint8_t *>(&ptr) + 6, 0x00, 2);
ptr &= ptr_mask;
return reinterpret_cast<void *>(ptr);
}
public:
small_ptr() {
std::memset(&bytes, 0x00, 6);
}
template<typename T>
explicit small_ptr(T * p) {
set(p);
}
template<typename T>
bool has_type() const {
return type() == TypeIndex<T, Types...>::value;
}
template<typename T>
T * get() {
void * p = has_type<T>() ? value() : nullptr;
return static_cast<T *>(p);
}
template<typename T>
T const * get() const {
void const * p = has_type<T>() ? value() : nullptr;
return static_cast<T const *>(p);
}
template<typename T>
void set(T * p) {
auto ptr = reinterpret_cast<uintptr_t>(p);
std::memcpy(bytes, &ptr, 6);
assert(!(bytes[0] & tag_mask) && "uintptr_t of pointer used lower bits; cannot proceed");
bytes[0] |= TypeIndex<T, Types...>::value;
}
};
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment