Pigweed
 
Loading...
Searching...
No Matches
packed_ptr.h
1// Copyright 2024 The Pigweed Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not
4// use this file except in compliance with the License. You may obtain a copy of
5// the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations under
13// the License.
14#pragma once
15
16#include <cstddef>
17#include <cstdint>
18
19#include "lib/stdcompat/bit.h"
20#include "pw_assert/assert.h"
21
22namespace pw {
23
32template <typename T>
33class PackedPtr {
34 public:
35 // Calculate the number of available bits in a function, since the type T may
36 // include fields of type `PackedPtr<T>` and not be fully defined when the
37 // `PackedPtr<T>` class is instantiated.
38 static constexpr size_t NumBits() {
39 CheckAlignment();
40 return 31 - cpp20::countl_zero(static_cast<uint32_t>(alignof(T)));
41 }
42
43 constexpr explicit PackedPtr() { CheckAlignment(); }
44 ~PackedPtr() = default;
45
46 constexpr PackedPtr(T* ptr, uintptr_t packed_value) : PackedPtr() {
47 set(ptr);
49 }
50
51 template <typename T2,
52 typename = std::enable_if_t<std::is_convertible_v<T2, T>>>
53 constexpr PackedPtr(const PackedPtr<T2>& other) {
54 *this = other;
55 }
56
57 template <typename T2,
58 typename = std::enable_if_t<std::is_convertible_v<T2, T>>>
59 constexpr PackedPtr& operator=(const PackedPtr<T2>& other) {
60 data_ = other.data_;
61 return *this;
62 }
63
64 template <typename T2,
65 typename = std::enable_if_t<std::is_convertible_v<T2, T>>>
66 constexpr PackedPtr(PackedPtr<T2>&& other) {
67 *this = std::move(other);
68 }
69
70 template <typename T2,
71 typename = std::enable_if_t<std::is_convertible_v<T2, T>>>
72 constexpr PackedPtr& operator=(PackedPtr<T2>&& other) {
73 data_ = std::exchange(other.data_, 0);
74 return *this;
75 }
76
77 constexpr T& operator*() const { return *(get()); }
78 constexpr T* operator->() const { return get(); }
79
81 constexpr T* get() const { return cpp20::bit_cast<T*>(data_ & ~kValueMask); }
82
84 constexpr uintptr_t packed_value() const { return data_ & kValueMask; }
85
87 constexpr void set(T* ptr) {
88 auto packed_ptr = cpp20::bit_cast<uintptr_t, T*>(ptr);
89 PW_DASSERT((packed_ptr & kValueMask) == 0);
90 data_ = packed_ptr | packed_value();
91 }
92
96 constexpr void set_packed_value(uintptr_t packed_value) {
97 PW_DASSERT(packed_value <= kValueMask);
98 data_ = (data_ & ~kValueMask) | packed_value;
99 }
100
101 private:
102 // Check the alignment of T in a function, since the type T may include fields
103 // of type `PackedPtr<T>` and not be fully defined when the `PackedPtr<T>`
104 // class is instantiated.
105 static constexpr void CheckAlignment() {
106 static_assert(alignof(T) > 1,
107 "Alignment must be more than one to pack any bits");
108 }
109
110 static constexpr uintptr_t kValueMask = (1 << NumBits()) - 1;
111
112 // Allows copying and moving between convertible types.
113 template <typename>
114 friend class PackedPtr;
115
116 uintptr_t data_ = 0;
117};
118
119} // namespace pw
Definition: packed_ptr.h:33
constexpr void set_packed_value(uintptr_t packed_value)
Definition: packed_ptr.h:96
constexpr void set(T *ptr)
Sets the pointer without changing the packed value.
Definition: packed_ptr.h:87
constexpr uintptr_t packed_value() const
Returns the packed_value packed into the unused bits of the pointer.
Definition: packed_ptr.h:84
constexpr T * get() const
Returns the pointer.
Definition: packed_ptr.h:81
Provides basic helpers for reading and writing UTF-8 encoded strings.
Definition: alignment.h:27