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
19namespace FASTER {
20/// Convert GCC's __builtin_clzl() to Microsoft's _BitScanReverse64().
21inline 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
31namespace FASTER {
32namespace core {
33
34/// Round the specified size up to the next power of 2.
35inline 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.
46constexpr 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.
55template <typename T>
56constexpr 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.
61template <typename T>
62using remove_const_t = typename std::remove_const<T>::type;
63
64/// alloc_aligned(): allocate a unique_ptr with a particular alignment.
65template <typename T>
66void 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
72template <typename T>
73struct AlignedDeleter {
74 void operator()(T* p) const {
75 unique_ptr_aligned_deleter(p);
76 }
77};
78
79template <typename T>
80using aligned_unique_ptr_t = std::unique_ptr<T, AlignedDeleter<T>>;
81static_assert(sizeof(aligned_unique_ptr_t<void>) == 8, "sizeof(unique_aligned_ptr_t)");
82
83template <typename T>
84aligned_unique_ptr_t<T> make_aligned_unique_ptr(T* p) {
85 return aligned_unique_ptr_t<T>(p, AlignedDeleter<T>());
86}
87
88template <typename T>
89aligned_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.
94template <typename T>
95void 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
101template <typename T>
102struct ContextDeleter {
103 void operator()(T* p) const {
104 unique_ptr_context_deleter(p);
105 }
106};
107
108template <typename T>
109using context_unique_ptr_t = std::unique_ptr<T, ContextDeleter<T>>;
110static_assert(sizeof(context_unique_ptr_t<void>) == 8, "sizeof(context_unique_ptr_t)");
111
112template <typename T>
113context_unique_ptr_t<T> make_context_unique_ptr(T* p) {
114 return context_unique_ptr_t<T>(p, ContextDeleter<T>());
115}
116
117template <typename T>
118context_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