1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef BERBERIS_BASE_POINTER_AND_COUNTER_H_
18 #define BERBERIS_BASE_POINTER_AND_COUNTER_H_
19 
20 #include <cstdint>
21 
22 #include "berberis/base/bit_util.h"
23 #include "berberis/base/checks.h"
24 
25 namespace berberis {
26 
27 // Pack aligned pointer and counter into uint64_t for atomic handling.
28 template <typename T, size_t kAlign = alignof(T)>
29 struct PointerAndCounter {
30 #if defined(__LP64__)
31   // 64-bit pointers and size_t. Use 48 bits for address and save alignment bits.
32   //     [counter][pointer-without-align-bits]
33   // bit: 63                                0
34   static_assert(sizeof(T*) == 8, "wrong pointer size");
35   static const size_t kPointerBits = 48;
36   static const size_t kAlignBits = BitUtilLog2(kAlign);
37 #else
38   // 32-bit pointers and size_t. KISS.
39   //     [counter][pointer]
40   // bit: 63   32  31    0
41   static_assert(sizeof(T*) == 4, "wrong pointer size");
42   static const size_t kPointerBits = 32;
43   static const size_t kAlignBits = 0;
44 #endif
45 
46   static const size_t kRealPointerBits = kPointerBits - kAlignBits;
47   static const size_t kCounterBits = 64 - kRealPointerBits;
48 
49   static const uint64_t kRealPointerMask = uint64_t(-1) >> kCounterBits;
50 
51   static const uint64_t kMaxCounter = uint64_t(1) << kCounterBits;
52 
53   // ATTENTION: counter might get truncated!
PackUnsafePointerAndCounter54   static uint64_t PackUnsafe(T* p, uint64_t cnt) {
55     uintptr_t ptr = reinterpret_cast<uintptr_t>(p);
56     return (static_cast<uint64_t>(ptr) >> kAlignBits) | (cnt << kRealPointerBits);
57   }
58 
PackPointerAndCounter59   static uint64_t Pack(T* p, uint64_t cnt) {
60     CHECK_GT(kMaxCounter, cnt);
61     return PackUnsafe(p, cnt);
62   }
63 
UnpackPointerPointerAndCounter64   static T* UnpackPointer(uint64_t v) {
65     uintptr_t ptr = static_cast<uintptr_t>((v & kRealPointerMask) << kAlignBits);
66     return reinterpret_cast<T*>(ptr);
67   }
68 
UnpackCounterPointerAndCounter69   static uint64_t UnpackCounter(uint64_t v) { return v >> kRealPointerBits; }
70 };
71 
72 }  // namespace berberis
73 
74 #endif  // BERBERIS_BASE_POINTER_AND_COUNTER_H_
75