Last active
August 29, 2015 14:27
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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