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>) {
32 return sizeof(T) == type.
memSize;
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 std::for_each_n(array, size, [&](
auto& item) { clear(item, type); });
147void deallocateArray(T* array)
noexcept {
148 UA_free(stripEmptyArraySentinel(array));
152void deallocateArray(T* array,
size_t size,
const UA_DataType& type)
noexcept {
153 assert(isValidTypeCombination<T>(type));
154 clearArray(array, size, type);
155 deallocateArray(array);
159[[nodiscard]] T* allocateArray(
size_t size) {
160 if (size > UA_INT32_MAX) {
161 throw std::bad_alloc{};
164 return makeEmptyArraySentinel<T>();
166 auto* ptr =
static_cast<T*
>(
UA_calloc(size,
sizeof(T)));
167 if (ptr ==
nullptr) {
168 throw std::bad_alloc{};
176 typename = std::enable_if_t<std::is_invocable_v<Deleter, T*>>>
177[[nodiscard]]
auto makeUniqueArray(
size_t size, Deleter&& deleter) {
179 return std::unique_ptr<T[], std::decay_t<Deleter>>{
180 allocateArray<T>(size), std::forward<Deleter>(deleter)
185[[nodiscard]]
auto makeUniqueArray(
size_t size,
const UA_DataType& type) {
186 return makeUniqueArray<T>(size, [&type, size](T* native) {
187 deallocateArray(native, size, type);
191template <
typename InputIt>
192[[nodiscard]] std::pair<IterValueT<InputIt>*,
size_t> copyArray(
193 InputIt first, InputIt last,
const UA_DataType& type, std::forward_iterator_tag
195 using ValueType = IterValueT<InputIt>;
196 const size_t size = std::distance(first, last);
197 auto dst = makeUniqueArray<ValueType>(size, type);
198 std::transform(first, last, dst.get(), [&](
auto&& item) {
199 return copy<ValueType>(std::forward<decltype(item)>(item), type);
201 return {dst.release(), size};
204template <
typename InputIt>
205[[nodiscard]] std::pair<IterValueT<InputIt>*,
size_t> copyArray(
206 InputIt first, InputIt last,
const UA_DataType& type, std::input_iterator_tag
208 using ValueType = IterValueT<InputIt>;
210 return {makeEmptyArraySentinel<ValueType>(), 0};
214 size_t capacity = 16;
215 auto dst = makeUniqueArray<ValueType>(capacity, [&](ValueType* ptr) {
216 deallocateArray(ptr, index, type);
219 const auto reallocate = [&](
size_t newSize) {
221 auto* ptr =
static_cast<ValueType*
>(
UA_realloc(dst.get(), newSize *
sizeof(ValueType)));
222 if (ptr ==
nullptr) {
223 throw std::bad_alloc{};
229 for (
auto it = first; it != last; ++it, ++index) {
230 if (index >= capacity) {
232 reallocate(capacity);
234 dst[index] = copy<ValueType>(*it, type);
237 return {dst.release(), index};
240template <
typename InputIt>
241[[nodiscard]] std::pair<IterValueT<InputIt>*,
size_t> copyArray(
242 InputIt first, InputIt last,
const UA_DataType& type
244 return copyArray(first, last, type, IterCategoryT<InputIt>{});
248[[nodiscard]] T* copyArray(
const T* src,
size_t size) {
249 if (isEmptyArray(src, size)) {
250 return makeEmptyArraySentinel<T>();
252 T* dst = allocateArray<T>(size);
253 std::memcpy(dst, src, size *
sizeof(T));
258[[nodiscard]] T* copyArray(
const T* src,
size_t size,
const UA_DataType& type) {
259 return IsPointerFree<T>::value
260 ? copyArray(src, size)
261 : 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.
UA_StatusCode UA_copy(const void *src, void *dst, const UA_DataType *type)
void UA_clear(void *p, const UA_DataType *type)