|
| 1 | +/* ----------------------------------------------------------------------------- |
| 2 | +Copyright (c) 2018 Jose L. Hidalgo (PpluX) |
| 3 | +
|
| 4 | + px_mem.h - Mem management functions |
| 5 | + Single header file to handle Mem references, Buffers, unique_ptr/array |
| 6 | + alike objects and so on. It offers safer objects to handle pointers to |
| 7 | + memory and a RAII implementation for dynamic allocated memory. |
| 8 | + |
| 9 | +Permission is hereby granted, free of charge, to any person obtaining a copy of |
| 10 | +this software and associated documentation files (the "Software"), to deal in |
| 11 | +the Software without restriction, including without limitation the rights to |
| 12 | +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of |
| 13 | +the Software, and to permit persons to whom the Software is furnished to do so, |
| 14 | +subject to the following conditions: |
| 15 | +
|
| 16 | +The above copyright notice and this permission notice shall be included in all |
| 17 | +copies or substantial portions of the Software. |
| 18 | +
|
| 19 | +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 20 | +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS |
| 21 | +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR |
| 22 | +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER |
| 23 | +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
| 24 | +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| 25 | +----------------------------------------------------------------------------- */ |
| 26 | + |
| 27 | +// USAGE |
| 28 | +// |
| 29 | +// In *ONE* C++ file you need to declare |
| 30 | +// #define PX_MEM_IMPLEMENTATION |
| 31 | +// before including the file that contains px_mem.h |
| 32 | + |
| 33 | + |
| 34 | +#ifndef PX_MEM |
| 35 | +#define PX_MEM 1 |
| 36 | + |
| 37 | +#include <cstddef> |
| 38 | +#include <cstdint> |
| 39 | +#include <memory> //> for std::align |
| 40 | + |
| 41 | +#ifndef PX_MEM_ASSERT |
| 42 | +#include <cassert> |
| 43 | +#define PX_MEM_ASSERT(cnd, msg) assert((cnd) && msg) |
| 44 | +#endif |
| 45 | + |
| 46 | +namespace px { |
| 47 | + |
| 48 | + // Set this first, before anything else, the default implementation will |
| 49 | + // use a wrapper around malloc and free |
| 50 | + void SetMemoryFunctions( |
| 51 | + void*(*MemAlloc)(size_t size_bytes, size_t alignment), |
| 52 | + void (*MemFree)(void*)); |
| 53 | + |
| 54 | + // Function to adquire memory (with proper alignment) |
| 55 | + void *MemoryAlloc(size_t amount, size_t alignment); |
| 56 | + |
| 57 | + // Function to release a previously adquired memory |
| 58 | + void MemoryFree(void *ptr); |
| 59 | + |
| 60 | + // Class to handle references to array of objects in memory |
| 61 | + template<class T> |
| 62 | + class ConstMemRef { |
| 63 | + public: |
| 64 | + ConstMemRef() : ptr_(nullptr), count_(0) {} |
| 65 | + ConstMemRef(const T* ptr, size_t num) : ptr_(ptr), count_(num) {} |
| 66 | + ConstMemRef(const ConstMemRef &) = default; |
| 67 | + ConstMemRef& operator=(const ConstMemRef &) = default; |
| 68 | + constexpr ConstMemRef(ConstMemRef &&) = default; |
| 69 | + ConstMemRef& operator=(ConstMemRef &&) = default; |
| 70 | + |
| 71 | + const T& operator[](size_t p) const { PX_MEM_ASSERT(p < count_, "Invalid access");return ptr_[p]; } |
| 72 | + size_t size() const { return count_; } |
| 73 | + size_t sizeInBytes() const { return count_*sizeof(T); } |
| 74 | + const T* get() const { return ptr_; } |
| 75 | + private: |
| 76 | + const T *ptr_; |
| 77 | + size_t count_; |
| 78 | + }; |
| 79 | + |
| 80 | + // Mem<T> similar to unique_ptr but with some restrictions to fully implement RAII and |
| 81 | + // avoid any new/delete |
| 82 | + template<class T> |
| 83 | + class Mem { |
| 84 | + public: |
| 85 | + Mem() {} |
| 86 | + ~Mem() { reset(); } |
| 87 | + |
| 88 | + Mem(Mem &&other) { |
| 89 | + ptr_ = other.ptr_; |
| 90 | + other.ptr_ = nullptr; |
| 91 | + } |
| 92 | + |
| 93 | + Mem& operator=(Mem &&other) { |
| 94 | + reset(); |
| 95 | + ptr_ = other.ptr_; |
| 96 | + other.ptr_ = nullptr; |
| 97 | + return *this; |
| 98 | + } |
| 99 | + |
| 100 | + bool valid() const { return ptr_ != nullptr; } |
| 101 | + operator bool() const { return valid(); } |
| 102 | + |
| 103 | + // instance an object of type T |
| 104 | + T* alloc() { reset(); ptr_ = new (MemoryAlloc(sizeof(T), alignof(T))) T(); return ptr_; } |
| 105 | + |
| 106 | + // alloc for derived classes |
| 107 | + template<class D> |
| 108 | + D* alloc() { |
| 109 | + reset(); |
| 110 | + D *result = new (MemoryAlloc(sizeof(D), alignof(T))) D(); |
| 111 | + ptr_ = result; |
| 112 | + return result; |
| 113 | + } |
| 114 | + |
| 115 | + void reset() { |
| 116 | + if (ptr_) { |
| 117 | + ptr_->~T(); |
| 118 | + MemoryFree(ptr_); |
| 119 | + ptr_ = nullptr; |
| 120 | + } |
| 121 | + } |
| 122 | + |
| 123 | + T* get() { return ptr_; } |
| 124 | + const T* get() const { return ptr_; } |
| 125 | + |
| 126 | + T* operator->() { return ptr_; } |
| 127 | + const T* operator->() const { return ptr_; } |
| 128 | + T& operator*() { return *ptr_; } |
| 129 | + const T& operator*() const { return *ptr_; } |
| 130 | + |
| 131 | + private: |
| 132 | + T *ptr_ = nullptr; |
| 133 | + }; |
| 134 | + |
| 135 | + |
| 136 | + |
| 137 | + template<class T> |
| 138 | + class Mem<T[]> { |
| 139 | + public: |
| 140 | + typedef T* iterator; |
| 141 | + typedef const T* const_iterator; |
| 142 | + |
| 143 | + Mem() {} |
| 144 | + ~Mem() { reset(); } |
| 145 | + |
| 146 | + Mem(Mem &&other) { |
| 147 | + ptr_ = other.ptr_; |
| 148 | + size_ = other.size_; |
| 149 | + other.ptr_ = nullptr; |
| 150 | + other.size_ = 0; |
| 151 | + } |
| 152 | + |
| 153 | + Mem& operator=(Mem &&other) { |
| 154 | + reset(); |
| 155 | + ptr_ = other.ptr_; |
| 156 | + size_ = other.size_; |
| 157 | + other.ptr_ = nullptr; |
| 158 | + other.size_ = 0; |
| 159 | + return *this; |
| 160 | + } |
| 161 | + |
| 162 | + bool valid() const { return ptr_ != nullptr; } |
| 163 | + operator bool() const { return valid(); } |
| 164 | + |
| 165 | + T* alloc(size_t num) { |
| 166 | + reset(); |
| 167 | + ptr_ = reinterpret_cast<T*>(MemoryAlloc(sizeof(T)*num, alignof(T))); |
| 168 | + size_ = num; |
| 169 | + for(size_t i = 0; i < size_; ++i) { |
| 170 | + new (&ptr_[i]) T(); |
| 171 | + } |
| 172 | + return ptr_; |
| 173 | + } |
| 174 | + |
| 175 | + void reset() { |
| 176 | + if (ptr_) { |
| 177 | + for(size_t i = 0; i < size_; ++i) { |
| 178 | + ptr_[i].~T(); |
| 179 | + } |
| 180 | + MemoryFree(ptr_); |
| 181 | + ptr_ = nullptr; |
| 182 | + size_ = 0; |
| 183 | + } |
| 184 | + } |
| 185 | + |
| 186 | + void copy(const T* begin, const T* end) { |
| 187 | + T *dst = alloc(((size_t)end-(size_t)begin)/sizeof(T)); |
| 188 | + for(const T *p = begin; p != end; ++p, ++dst) { |
| 189 | + *dst = *p; |
| 190 | + } |
| 191 | + } |
| 192 | + |
| 193 | + void copy(ConstMemRef<T> memref) { |
| 194 | + if (memref.size() == 0) { |
| 195 | + reset(); |
| 196 | + } else { |
| 197 | + T *dst = alloc(memref.size()); |
| 198 | + for(size_t i = 0; i < memref.size(); ++i) { |
| 199 | + dst[i] = memref[i]; |
| 200 | + } |
| 201 | + } |
| 202 | + } |
| 203 | + |
| 204 | + T& operator[](size_t i) { |
| 205 | + PX_MEM_ASSERT(i < size_, "Invalid Access"); |
| 206 | + return ptr_[i]; |
| 207 | + } |
| 208 | + |
| 209 | + const T& operator[](size_t i) const { |
| 210 | + PX_MEM_ASSERT(i < size_, "Invalid Access"); |
| 211 | + return ptr_[i]; |
| 212 | + } |
| 213 | + |
| 214 | + iterator begin() const { |
| 215 | + return ptr_; |
| 216 | + } |
| 217 | + iterator end() const { |
| 218 | + return ptr_+size_; |
| 219 | + } |
| 220 | + const_iterator cbegin() const { |
| 221 | + return ptr_; |
| 222 | + } |
| 223 | + const_iterator cend() const { |
| 224 | + return ptr_+size_; |
| 225 | + } |
| 226 | + |
| 227 | + size_t size() const { return size_; } |
| 228 | + size_t sizeInBytes() const { return size_*sizeof(T); } |
| 229 | + |
| 230 | + T* get() { return ptr_; } |
| 231 | + const T* get() const { return ptr_; } |
| 232 | + |
| 233 | + operator ConstMemRef<T>() const { return ref(); } |
| 234 | + ConstMemRef<T> ref() const { return ConstMemRef<T>(ptr_, size()); } |
| 235 | + |
| 236 | + private: |
| 237 | + T *ptr_ = nullptr; |
| 238 | + size_t size_ = 0; |
| 239 | + }; |
| 240 | + |
| 241 | + // minimal standard allocator that uses MemoryAlloc/MemoryFree functions |
| 242 | + template <class T> |
| 243 | + struct Allocator { |
| 244 | + typedef T value_type; |
| 245 | + Allocator() {} |
| 246 | + template <class U> Allocator(const Allocator<U>& other) {} |
| 247 | + T* allocate(std::size_t n) { |
| 248 | + return (T*)MemoryAlloc(sizeof(T)*n, alignof(T)); |
| 249 | + } |
| 250 | + void deallocate(T* p, std::size_t n) { |
| 251 | + MemoryFree(p); |
| 252 | + } |
| 253 | + }; |
| 254 | + template <class T, class U> |
| 255 | + bool operator==(const Allocator<T>&, const Allocator<U>&) { return false; } |
| 256 | + template <class T, class U> |
| 257 | + bool operator!=(const Allocator<T>&, const Allocator<U>&) { return true; } |
| 258 | + |
| 259 | +#ifdef PX_MEM_IMPLEMENTATION |
| 260 | + namespace { |
| 261 | + void* _DefaultMemoryAlloc(size_t mem_size, size_t align) { |
| 262 | + size_t mem_plus_align = mem_size+align; |
| 263 | + void *raw_mem = std::malloc(mem_plus_align+sizeof(void*)); |
| 264 | + void *ptr = ((char*)raw_mem)+sizeof(void*); |
| 265 | + void *result = std::align(align, mem_size, ptr , mem_plus_align); |
| 266 | + PX_MEM_ASSERT(result != nullptr, "Default Memory Alloc failed"); |
| 267 | + ((void**)result)[-1] = raw_mem; |
| 268 | + return result; |
| 269 | + } |
| 270 | + |
| 271 | + void _DefaultMemoryFree(void *ptr) { |
| 272 | + void *raw_mem = ((void**)ptr)[-1]; |
| 273 | + std::free(raw_mem); |
| 274 | + } |
| 275 | + } |
| 276 | + |
| 277 | + static struct { |
| 278 | + void *(*alloc)(size_t, size_t ) = _DefaultMemoryAlloc; |
| 279 | + void (*free)(void*) = _DefaultMemoryFree; |
| 280 | + } GLOBAL_mem; |
| 281 | + |
| 282 | + void *MemoryAlloc(size_t amount, size_t alignment) { |
| 283 | + return GLOBAL_mem.alloc(amount, alignment); |
| 284 | + } |
| 285 | + |
| 286 | + void MemoryFree(void *ptr) { |
| 287 | + return GLOBAL_mem.free(ptr); |
| 288 | + } |
| 289 | + |
| 290 | + void SetMemoryFunctions( void*(*MemAlloc)(size_t, size_t), void (*MemFree)(void*)) { |
| 291 | + if (!MemAlloc && !MemFree) { |
| 292 | + MemAlloc = _DefaultMemoryAlloc; |
| 293 | + MemFree = _DefaultMemoryFree; |
| 294 | + } |
| 295 | + GLOBAL_mem.alloc = MemAlloc; |
| 296 | + GLOBAL_mem.free = MemFree; |
| 297 | + } |
| 298 | +#endif |
| 299 | + |
| 300 | +} // px |
| 301 | + |
| 302 | +#endif |
0 commit comments