1 | // Copyright (c) Microsoft Corporation. All rights reserved. |
2 | // Licensed under the MIT license. |
3 | |
4 | #pragma once |
5 | |
6 | #include <cassert> |
7 | #include <cstdint> |
8 | #include <functional> |
9 | #include <memory> |
10 | #include <type_traits> |
11 | |
12 | #include "alloc.h" |
13 | #include "lss_allocator.h" |
14 | |
15 | #ifdef _WIN32 |
16 | #include <intrin.h> |
17 | #pragma intrinsic(_BitScanReverse64) |
18 | #else |
19 | namespace FASTER { |
20 | /// Convert GCC's __builtin_clzl() to Microsoft's _BitScanReverse64(). |
21 | inline uint8_t _BitScanReverse64(unsigned long* index, uint64_t mask) { |
22 | bool found = mask > 0; |
23 | *index = 63 - __builtin_clzl(mask); |
24 | return found; |
25 | } |
26 | } |
27 | #endif |
28 | |
29 | /// Wrappers for C++ std::unique_ptr<>. |
30 | |
31 | namespace FASTER { |
32 | namespace core { |
33 | |
34 | /// Round the specified size up to the next power of 2. |
35 | inline size_t next_power_of_two(size_t size) { |
36 | assert(size > 0); |
37 | // BSR returns the index k of the most-significant 1 bit. So 2^(k+1) > (size - 1) >= 2^k, |
38 | // which means 2^(k+1) >= size > 2^k. |
39 | unsigned long k; |
40 | uint8_t found = _BitScanReverse64(&k, size - 1); |
41 | return (uint64_t)1 << (found * (k + 1)); |
42 | } |
43 | |
44 | /// Pad alignment to specified. Declared "constexpr" so that the calculation can be performed at |
45 | /// compile time, assuming parameters "size" and "alignment" are known then. |
46 | constexpr inline size_t pad_alignment(size_t size, size_t alignment) { |
47 | assert(alignment > 0); |
48 | // Function implemented only for powers of 2. |
49 | assert((alignment & (alignment - 1)) == 0); |
50 | size_t max_padding = alignment - 1; |
51 | return (size + max_padding) & ~max_padding; |
52 | } |
53 | |
54 | /// Pad alignment to specified type. |
55 | template <typename T> |
56 | constexpr inline size_t pad_alignment(size_t size) { |
57 | return pad_alignment(size, alignof(T)); |
58 | } |
59 | |
60 | /// Defined in C++ 14; copying the definition here for older compilers. |
61 | template <typename T> |
62 | using remove_const_t = typename std::remove_const<T>::type; |
63 | |
64 | /// alloc_aligned(): allocate a unique_ptr with a particular alignment. |
65 | template <typename T> |
66 | void unique_ptr_aligned_deleter(T* p) { |
67 | auto q = const_cast<remove_const_t<T>*>(p); |
68 | q->~T(); |
69 | aligned_free(q); |
70 | } |
71 | |
72 | template <typename T> |
73 | struct AlignedDeleter { |
74 | void operator()(T* p) const { |
75 | unique_ptr_aligned_deleter(p); |
76 | } |
77 | }; |
78 | |
79 | template <typename T> |
80 | using aligned_unique_ptr_t = std::unique_ptr<T, AlignedDeleter<T>>; |
81 | static_assert(sizeof(aligned_unique_ptr_t<void>) == 8, "sizeof(unique_aligned_ptr_t)" ); |
82 | |
83 | template <typename T> |
84 | aligned_unique_ptr_t<T> make_aligned_unique_ptr(T* p) { |
85 | return aligned_unique_ptr_t<T>(p, AlignedDeleter<T>()); |
86 | } |
87 | |
88 | template <typename T> |
89 | aligned_unique_ptr_t<T> alloc_aligned(size_t alignment, size_t size) { |
90 | return make_aligned_unique_ptr<T>(reinterpret_cast<T*>(aligned_alloc(alignment, size))); |
91 | } |
92 | |
93 | /// alloc_context(): allocate a small chunk of memory for a callback context. |
94 | template <typename T> |
95 | void unique_ptr_context_deleter(T* p) { |
96 | auto q = const_cast<remove_const_t<T>*>(p); |
97 | q->~T(); |
98 | lss_allocator.Free(q); |
99 | } |
100 | |
101 | template <typename T> |
102 | struct ContextDeleter { |
103 | void operator()(T* p) const { |
104 | unique_ptr_context_deleter(p); |
105 | } |
106 | }; |
107 | |
108 | template <typename T> |
109 | using context_unique_ptr_t = std::unique_ptr<T, ContextDeleter<T>>; |
110 | static_assert(sizeof(context_unique_ptr_t<void>) == 8, "sizeof(context_unique_ptr_t)" ); |
111 | |
112 | template <typename T> |
113 | context_unique_ptr_t<T> make_context_unique_ptr(T* p) { |
114 | return context_unique_ptr_t<T>(p, ContextDeleter<T>()); |
115 | } |
116 | |
117 | template <typename T> |
118 | context_unique_ptr_t<T> alloc_context(uint32_t size) { |
119 | return make_context_unique_ptr<T>(reinterpret_cast<T*>(lss_allocator.Allocate(size))); |
120 | } |
121 | |
122 | } |
123 | } // namespace FASTER::core |
124 | |