#ifndef INCLUDED_ALLOCATORS_UNIQUE_RANGE #define INCLUDED_ALLOCATORS_UNIQUE_RANGE #include "lib/lib_api.h" // we usually don't hold multiple references to allocations, so unique_ptr // can be used instead of the more complex (ICC generated incorrect code on // 2 occasions) and expensive shared_ptr. // a custom deleter is required because allocators such as ReserveAddressSpace need to // pass the size to their deleter. we want to mix pointers from various allocators, but // unique_ptr's deleter is fixed at compile-time, so it would need to be general enough // to handle all allocators. // storing the size and a function pointer would be one such solution, with the added // bonus of no longer requiring a complete type at the invocation of ~unique_ptr. // however, this inflates the pointer size to 3 words. if only a few allocator types // are needed, we can replace the function pointer with an index stashed into the // lower bits of the pointer (safe because allocations are always aligned to the // word size). typedef intptr_t IdxDeleter; // no-op deleter (use when returning part of an existing allocation) // must be zero because reset() sets address (which includes idxDeleter) to zero. static const IdxDeleter idxDeleterNone = 0; static const IdxDeleter idxDeleterAligned = 1; // (temporary value to prevent concurrent calls to AddUniqueRangeDeleter) static const IdxDeleter idxDeleterBusy = -IdxDeleter(1); // governs the maximum number of IdxDeleter and each pointer's alignment requirements static const IdxDeleter idxDeleterBits = 0x7; typedef void (*UniqueRangeDeleter)(void* pointer, size_t size); /** * @return the next available IdxDeleter and associate it with the deleter. * halts the program if the idxDeleterBits limit has been reached. * * thread-safe, but no attempt is made to detect whether the deleter has already been * registered (would require a mutex). each allocator must ensure they only call this once. **/ LIB_API IdxDeleter AddUniqueRangeDeleter(UniqueRangeDeleter deleter); LIB_API void CallUniqueRangeDeleter(void* pointer, size_t size, IdxDeleter idxDeleter) throw(); // unfortunately, unique_ptr allows constructing without a custom deleter. to ensure callers can // rely upon pointers being associated with a size, we introduce a `UniqueRange' replacement. // its interface is identical to unique_ptr except for the constructors, the addition of // size() and the removal of operator bool (which avoids implicit casts to int). class UniqueRange { public: typedef void* pointer; typedef void element_type; UniqueRange() { Set(0, 0, idxDeleterNone); } UniqueRange(pointer p, size_t size, IdxDeleter deleter) { Set(p, size, deleter); } UniqueRange(RVALUE_REF(UniqueRange) rvalue) { UniqueRange& lvalue = LVALUE(rvalue); address_ = lvalue.address_; size_ = lvalue.size_; lvalue.address_ = 0; } UniqueRange& operator=(RVALUE_REF(UniqueRange) rvalue) { UniqueRange& lvalue = LVALUE(rvalue); if(this != &lvalue) { Delete(); address_ = lvalue.address_; size_ = lvalue.size_; lvalue.address_ = 0; } return *this; } ~UniqueRange() { Delete(); } pointer get() const { return pointer(address_ & ~idxDeleterBits); } IdxDeleter get_deleter() const { return IdxDeleter(address_ & idxDeleterBits); } size_t size() const { return size_; } // side effect: subsequent get_deleter will return idxDeleterNone pointer release() // relinquish ownership { pointer ret = get(); address_ = 0; return ret; } void reset() { Delete(); address_ = 0; } void reset(pointer p, size_t size, IdxDeleter deleter) { Delete(); Set(p, size, deleter); } void swap(UniqueRange& rhs) { std::swap(address_, rhs.address_); std::swap(size_, rhs.size_); } // don't define construction and assignment from lvalue, // but the declarations must be accessible UniqueRange(const UniqueRange&); UniqueRange& operator=(const UniqueRange&); private: void Set(pointer p, size_t size, IdxDeleter deleter) { ASSERT((uintptr_t(p) & idxDeleterBits) == 0); ASSERT(deleter <= idxDeleterBits); address_ = uintptr_t(p) | deleter; size_ = size; ASSERT(get() == p); ASSERT(get_deleter() == deleter); ASSERT(this->size() == size); } void Delete() { CallUniqueRangeDeleter(get(), size(), get_deleter()); } // (IdxDeleter is stored in the lower bits of address since size might not even be a multiple of 4.) uintptr_t address_; size_t size_; }; namespace std { static inline void swap(UniqueRange& p1, UniqueRange& p2) { p1.swap(p2); } static inline void swap(RVALUE_REF(UniqueRange) p1, UniqueRange& p2) { p2.swap(LVALUE(p1)); } static inline void swap(UniqueRange& p1, RVALUE_REF(UniqueRange) p2) { p1.swap(LVALUE(p2)); } } #endif // #ifndef INCLUDED_ALLOCATORS_UNIQUE_RANGE