16namespace opcua::detail {
21using IsPointerFree = std::disjunction<
23 std::is_floating_point<T>,
25 std::is_same<T, UA_Guid>>;
28constexpr bool isValidTypeCombination(
const UA_DataType& type) {
29 if constexpr (std::is_void_v<T>) {
37constexpr void clear(T& native,
const UA_DataType& type)
noexcept {
38 assert(isValidTypeCombination<T>(type));
39 if constexpr (IsPointerFree<T>::value) {
47void deallocate(T* native)
noexcept {
52void deallocate(T* native,
const UA_DataType& type)
noexcept {
53 assert(isValidTypeCombination<T>(type));
54 if (native !=
nullptr) {
61[[nodiscard]] T* allocate() {
62 auto* ptr =
static_cast<T*
>(
UA_calloc(1,
sizeof(T)));
64 throw std::bad_alloc{};
72 typename = std::enable_if_t<std::is_invocable_v<Deleter, T*>>>
73[[nodiscard]]
auto makeUnique(Deleter&& deleter) {
74 return std::unique_ptr<T, std::decay_t<Deleter>>{allocate<T>(), std::forward<Deleter>(deleter)};
78[[nodiscard]]
auto makeUnique(
const UA_DataType& type) {
79 return makeUnique<T>([&type](T* native) { deallocate(native, type); });
83[[nodiscard]]
constexpr T copy(
const T& src,
const UA_DataType& type) {
84 assert(isValidTypeCombination<T>(type));
85 if constexpr (IsPointerFree<T>::value) {
95[[nodiscard]]
constexpr auto copy(T&& src,
const UA_DataType& type) {
96 if constexpr (std::is_rvalue_reference_v<
decltype(src)>) {
97 return std::forward<T>(src);
99 return copy(std::as_const(src), type);
116inline constexpr uintptr_t emptyArraySentinel = 0x01;
119[[nodiscard]] T* makeEmptyArraySentinel() noexcept {
120 return reinterpret_cast<T*
>(emptyArraySentinel);
124[[nodiscard]] T* stripEmptyArraySentinel(T* array)
noexcept {
126 return reinterpret_cast<T*
>(
reinterpret_cast<uintptr_t
>(array) & ~emptyArraySentinel);
130bool isEmptyArray(T* array,
size_t size)
noexcept {
131 return stripEmptyArraySentinel(array) ==
nullptr || size == 0;
135void clearArray(T* array,
size_t size,
const UA_DataType& type)
noexcept {
136 if (isEmptyArray(array, size)) {
139 if constexpr (IsPointerFree<T>::value) {
140 std::memset(array, 0, size *
sizeof(T));
142 for (
size_t i = 0; i < size; ++i) {
143 clear(array[i], type);
149void deallocateArray(T* array)
noexcept {
150 UA_free(stripEmptyArraySentinel(array));
154void deallocateArray(T* array,
size_t size,
const UA_DataType& type)
noexcept {
155 assert(isValidTypeCombination<T>(type));
156 clearArray(array, size, type);
157 deallocateArray(array);
161[[nodiscard]] T* allocateArray(
size_t size) {
162 if (size > UA_INT32_MAX) {
163 throw std::bad_alloc{};
166 return makeEmptyArraySentinel<T>();
168 auto* ptr =
static_cast<T*
>(
UA_calloc(size,
sizeof(T)));
169 if (ptr ==
nullptr) {
170 throw std::bad_alloc{};
178 typename = std::enable_if_t<std::is_invocable_v<Deleter, T*>>>
179[[nodiscard]]
auto makeUniqueArray(
size_t size, Deleter&& deleter) {
181 return std::unique_ptr<T[], std::decay_t<Deleter>>{
182 allocateArray<T>(size), std::forward<Deleter>(deleter)
187[[nodiscard]]
auto makeUniqueArray(
size_t size,
const UA_DataType& type) {
188 return makeUniqueArray<T>(size, [&type, size](T* native) {
189 deallocateArray(native, size, type);
193template <
typename InputIt>
194[[nodiscard]] std::pair<IterValueT<InputIt>*,
size_t> copyArray(
195 InputIt first, InputIt last,
const UA_DataType& type, std::forward_iterator_tag
197 using ValueType = IterValueT<InputIt>;
198 const size_t size = std::distance(first, last);
199 auto dst = makeUniqueArray<ValueType>(size, type);
200 std::transform(first, last,
dst.get(), [&](
auto&& item) {
201 return copy<ValueType>(std::forward<decltype(item)>(item), type);
203 return {
dst.release(), size};
206template <
typename InputIt>
207[[nodiscard]] std::pair<IterValueT<InputIt>*,
size_t> copyArray(
208 InputIt first, InputIt last,
const UA_DataType& type, std::input_iterator_tag
210 using ValueType = IterValueT<InputIt>;
212 return {makeEmptyArraySentinel<ValueType>(), 0};
216 size_t capacity = 16;
217 auto dst = makeUniqueArray<ValueType>(capacity, [&](ValueType* ptr) {
218 deallocateArray(ptr, index, type);
221 const auto reallocate = [&](
size_t newSize) {
223 auto* ptr =
static_cast<ValueType*
>(
UA_realloc(
dst.get(), newSize *
sizeof(ValueType)));
224 if (ptr ==
nullptr) {
225 throw std::bad_alloc{};
231 for (
auto it = first; it != last; ++it, ++index) {
232 if (index >= capacity) {
234 reallocate(capacity);
236 dst[index] = copy<ValueType>(*it, type);
239 return {
dst.release(), index};
242template <
typename InputIt>
243[[nodiscard]] std::pair<IterValueT<InputIt>*,
size_t> copyArray(
244 InputIt first, InputIt last,
const UA_DataType& type
246 return copyArray(first, last, type, IterCategoryT<InputIt>{});
250[[nodiscard]] T* copyArray(
const T* src,
size_t size) {
251 if (isEmptyArray(src, size)) {
252 return makeEmptyArraySentinel<T>();
254 T*
dst = allocateArray<T>(size);
255 std::memcpy(dst, src, size *
sizeof(T));
260[[nodiscard]] T* copyArray(
const T* src,
size_t size,
const UA_DataType& type) {
261 return IsPointerFree<T>::value
262 ? copyArray(src, size)
263 : copyArray(src, src + size,
type).first;
constexpr void throwIfBad(UA_StatusCode code)
Check the status code and throw a BadStatus exception if the status code is bad.
void UA_clear(void *p, const UA_DataType *type)