HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
placement.hpp
1// Copyright Take Vos 2019, 2021-2022.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
7#include "utility.hpp"
8#include "type_traits.hpp"
9#include "cast.hpp"
10#include "exception.hpp"
11#include "check.hpp"
12#include <span>
13
14hi_warning_push();
15// C26492: Don't use const_cast to cast away const or volatile (type.3).
16// placement new requires non-const pointer, even if the non-constructor initializer doesn't
17// modify the memory.
18hi_warning_ignore_msvc(26492)
19
20namespace hi::inline v1 {
21
22template<typename T>
23inline bool check_alignment(void const *ptr) noexcept
24{
25 return std::bit_cast<uintptr_t>(ptr) % alignof(T) == 0;
26}
27
28template<typename T, typename Byte>
29class placement_ptr {
30 static_assert(
31 std::is_same_v<std::remove_cv_t<Byte>, std::byte> || std::is_same_v<std::remove_cv_t<Byte>, char> ||
32 std::is_same_v<std::remove_cv_t<Byte>, unsigned char> || std::is_same_v<std::remove_cv_t<Byte>, signed char>,
33 "Byte must be a byte type");
34 static_assert(std::is_trivially_constructible_v<T>);
35 static_assert(!std::is_const_v<Byte> || std::is_trivially_destructible_v<T>);
36
37 using value_type = copy_cv_t<T, Byte>;
38 value_type *ptr;
39
40public:
41 placement_ptr(std::span<Byte> bytes, std::size_t& offset)
42 {
43 Byte *_ptr = bytes.data() + offset;
44 offset += sizeof(T);
45 ptr = new (const_cast<std::remove_const_t<Byte> *>(_ptr)) T;
46 }
47
48 ~placement_ptr()
49 {
50 std::destroy_at(ptr);
51 }
52
53 placement_ptr() = delete;
54 placement_ptr(placement_ptr const&) = delete;
55 placement_ptr(placement_ptr&&) = delete;
56 placement_ptr& operator=(placement_ptr const&) = delete;
57 placement_ptr& operator=(placement_ptr&&) = delete;
58
59 value_type *operator->() const noexcept
60 {
61 return ptr;
62 }
63
64 value_type& operator*() const noexcept
65 {
66 return *ptr;
67 }
68};
69
70template<typename T, typename Byte>
71placement_ptr<T, Byte> unsafe_make_placement_ptr(std::span<Byte> bytes, std::size_t& offset)
72{
73 return placement_ptr<T, Byte>(bytes, offset);
74}
75
76template<typename T, typename Byte>
77placement_ptr<T, Byte> unsafe_make_placement_ptr(std::span<Byte> bytes, std::size_t&& offset = 0)
78{
79 std::size_t _offset = offset;
80 return unsafe_make_placement_ptr<T>(bytes, _offset);
81}
82
83template<typename T, typename Byte>
84bool check_placement_ptr(std::span<Byte> bytes, std::size_t offset = 0)
85{
86 return check_alignment<T>(bytes.data()) && (offset + sizeof(T) <= size(bytes));
87}
88
89template<typename T, typename Byte>
90placement_ptr<T, Byte> make_placement_ptr(std::span<Byte> bytes, std::size_t& offset)
91{
92 hi_parse_check(check_placement_ptr<T>(bytes, offset), "Parsing beyond end of buffer");
93 return placement_ptr<T, Byte>(bytes, offset);
94}
95
96template<typename T, typename Byte>
97placement_ptr<T, Byte> make_placement_ptr(std::span<Byte> bytes, std::size_t&& offset = 0)
98{
99 std::size_t _offset = offset;
100 return make_placement_ptr<T>(bytes, _offset);
101}
102
103template<typename T, typename Byte>
104class placement_array {
105 static_assert(
106 std::is_same_v<std::remove_cv_t<Byte>, std::byte> || std::is_same_v<std::remove_cv_t<Byte>, char> ||
107 std::is_same_v<std::remove_cv_t<Byte>, unsigned char> || std::is_same_v<std::remove_cv_t<Byte>, signed char>,
108 "Byte must be a byte type");
109 static_assert(std::is_trivially_constructible_v<T>);
110 static_assert(!std::is_const_v<T> || std::is_trivially_destructible_v<T>);
111
112 using value_type = copy_cv_t<T, Byte>;
113
114 Byte *_begin;
115 Byte *_end;
116
117public:
118 placement_array(std::span<Byte> bytes, std::size_t& offset, std::size_t n)
119 {
120 hilet bytes_ = bytes.data();
121
122 _begin = bytes_ + offset;
123 offset += sizeof(T) * n;
124 _end = bytes_ + offset;
125
126 for (auto i = 0_uz; i < n; i++) {
127 [[maybe_unused]] auto *ptr = new (const_cast<std::remove_cv_t<Byte> *>(_begin + i * sizeof(T))) T;
128 }
129 }
130
131 placement_array(placement_array const&) = delete;
132 placement_array(placement_array&&) = delete;
133 placement_array& operator=(placement_array const&) = delete;
134 placement_array& operator=(placement_array&&) = delete;
135
136 ~placement_array()
137 {
138 std::destroy(begin(), end());
139 }
140
141 std::size_t size() const noexcept
142 {
143 return std::distance(begin(), end());
144 }
145
146 bool contains(std::size_t index) const noexcept
147 {
148 return index < size();
149 }
150
151 value_type *begin() const noexcept
152 {
153 return std::launder(reinterpret_cast<value_type *>(_begin));
154 }
155
156 value_type *end() const noexcept
157 {
158 return std::launder(reinterpret_cast<value_type *>(_end));
159 }
160
161 value_type& operator[](ssize_t offset) const noexcept
162 {
163 return *(begin() + offset);
164 }
165};
166
167template<typename T, typename Byte>
168placement_array<T, Byte> unsafe_make_placement_array(std::span<Byte> bytes, std::size_t& offset, std::size_t n)
169{
170 return placement_array<T, Byte>(bytes, offset, n);
171}
172
173template<typename T, typename Byte>
174placement_array<T, Byte> unsafe_make_placement_array(std::span<Byte> bytes, std::size_t&& offset, std::size_t n)
175{
176 std::size_t _offset = offset;
177 return unsafe_make_placement_array<T>(bytes, _offset, n);
178}
179
180template<typename T, typename Byte>
181placement_array<T, Byte> unsafe_make_placement_array(std::span<Byte> bytes, std::size_t& offset)
182{
183 hilet n = bytes.size() / sizeof(T);
184 return unsafe_make_placement_array<T>(bytes, offset, n);
185}
186
187template<typename T, typename Byte>
188placement_array<T, Byte> unsafe_make_placement_array(std::span<Byte> bytes, std::size_t&& offset = 0)
189{
190 std::size_t _offset = offset;
191 return unsafe_make_placement_array<T>(bytes, _offset);
192}
193
194template<typename T, typename Byte>
195bool check_placement_array(std::span<Byte> bytes, std::size_t offset, std::size_t n)
196{
197 return check_alignment<T>(bytes.data()) && (offset + (n * sizeof(T)) <= bytes.size());
198}
199
200template<typename T, typename Byte>
201bool check_placement_array(std::span<Byte> bytes, std::size_t offset)
202{
203 return check_alignment<T>(bytes.data());
204}
205
206template<typename T, typename Byte>
207placement_array<T, Byte> make_placement_array(std::span<Byte> bytes, std::size_t& offset, std::size_t n)
208{
209 hi_parse_check(check_placement_array<T>(bytes, offset, n), "Parsing beyond end of buffer");
210 return placement_array<T, Byte>(bytes, offset, n);
211}
212
213template<typename T, typename Byte>
214placement_array<T, Byte> make_placement_array(std::span<Byte> bytes, std::size_t&& offset, std::size_t n)
215{
216 std::size_t _offset = offset;
217 return make_placement_array<T>(bytes, _offset, n);
218}
219
220template<typename T, typename Byte>
221placement_array<T, Byte> make_placement_array(std::span<Byte> bytes, std::size_t& offset)
222{
223 hilet n = bytes.size() / ssizeof(T);
224 return make_placement_array<T>(bytes, offset, n);
225}
226
227template<typename T, typename Byte>
228placement_array<T, Byte> make_placement_array(std::span<Byte> bytes, std::size_t&& offset = 0)
229{
230 std::size_t _offset = offset;
231 return make_placement_array<T>(bytes, _offset);
232}
233
234} // namespace hi::inline v1
Utilities used by the HikoGUI library itself.
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
DOXYGEN BUG.
Definition algorithm.hpp:15
The HikoGUI namespace.
Definition ascii.hpp:19
T begin(T... args)
T distance(T... args)
T end(T... args)