sparrow 2.6.0
C++20 idiomatic APIs for the Apache Arrow Columnar Format
Loading...
Searching...
No Matches
union_array.hpp
Go to the documentation of this file.
1// Copyright 2024 Man Group Operations Limited
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://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,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#pragma once
16
17#include <algorithm>
18#include <array>
19#include <charconv>
20#include <cstdint>
21#include <functional>
22#include <iterator>
23#include <limits>
24#include <numeric>
25#include <optional>
26#include <ranges>
27#include <span>
28#include <string>
29#include <string_view>
30#include <type_traits>
31#include <utility>
32#include <variant>
33#include <vector>
34
35#include "sparrow/array_api.hpp"
44#include "sparrow/u8_buffer.hpp"
52
53namespace sparrow
54{
57
58 namespace detail
59 {
60 template <>
62 {
63 [[nodiscard]] static constexpr sparrow::data_type get()
64 {
66 }
67 };
68
69 template <>
71 {
72 [[nodiscard]] static constexpr sparrow::data_type get()
73 {
75 }
76 };
77
78 inline constexpr std::size_t union_no_child = std::numeric_limits<std::size_t>::max();
79
88 {
89 std::size_t child = union_no_child;
90 std::size_t row = 0;
91 bool force_null = false;
92 bool inserted = false;
93 };
94
103 {
104 std::vector<std::vector<union_rebuild_value>> child_values;
107 };
108 }
109
115 template <class T>
116 constexpr bool is_dense_union_array_v = std::same_as<T, dense_union_array>;
117
123 template <class T>
124 constexpr bool is_sparse_union_array_v = std::same_as<T, sparse_union_array>;
125
170 template <class DERIVED>
171 class union_array_crtp_base : public crtp_base<DERIVED>
172 {
173 public:
174
176 using derived_type = DERIVED;
184 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
185 using size_type = std::size_t;
186
188
197 [[nodiscard]] constexpr std::optional<std::string_view> name() const;
198
207 [[nodiscard]] SPARROW_CONSTEXPR_CLANG std::optional<key_value_view> metadata() const;
208
222
234
246
257
268
276 [[nodiscard]] constexpr bool empty() const;
277
286 [[nodiscard]] constexpr size_type size() const;
287
296 [[nodiscard]] constexpr iterator begin();
297
306 [[nodiscard]] constexpr iterator end();
307
316 [[nodiscard]] constexpr const_iterator begin() const;
317
326 [[nodiscard]] constexpr const_iterator end() const;
327
336 [[nodiscard]] constexpr const_iterator cbegin() const;
337
346 [[nodiscard]] constexpr const_iterator cend() const;
347
356 [[nodiscard]] constexpr const_reverse_iterator rbegin() const;
357
366 [[nodiscard]] constexpr const_reverse_iterator rend() const;
367
376 [[nodiscard]] constexpr const_reverse_iterator crbegin() const;
377
386 [[nodiscard]] constexpr const_reverse_iterator crend() const;
387
394
401
406 template <std::input_iterator InputIt>
407 iterator insert(const_iterator pos, InputIt first, InputIt last, size_type count = 1);
408
414
420
425
430
434 void resize(size_type new_length);
435
439 void resize(size_type new_length, const_reference value);
440
444 void resize(size_type new_length, const array_traits::value_type& value);
445
450 constexpr void zero_null_values(const inner_value_type& value)
451 {
452 sparrow::zero_null_values(*this, value);
453 }
454
455 protected:
456
457 static constexpr size_t TYPE_ID_MAP_SIZE = 256;
458
459 using type_id_map = std::array<std::uint8_t, TYPE_ID_MAP_SIZE>;
460
469 static std::vector<std::uint8_t> child_type_ids_from_format(std::string_view format_string);
470
477 static constexpr type_id_map make_type_id_map(std::span<const std::uint8_t> child_type_ids);
478
489 template <std::ranges::input_range R>
490 static constexpr type_id_map
491 type_id_map_from_child_to_type_id(const std::optional<R>& child_index_to_type_id);
492
508 template <std::ranges::input_range R>
509 requires(std::convertible_to<std::ranges::range_value_t<R>, std::uint8_t>)
510 static constexpr std::string
511 make_format_string(bool dense, std::size_t n, const std::optional<R>& child_index_to_type_id);
512
513 using children_type = std::vector<cloning_ptr<array_wrapper>>;
514
526
539
549 constexpr union_array_crtp_base(const self_type& rhs);
550
562 constexpr self_type& operator=(const self_type& rhs);
563
564 constexpr union_array_crtp_base(self_type&& rhs) = default;
565 constexpr self_type& operator=(self_type&& rhs) = default;
566
567 template <class VALUE>
568 void resize_impl(size_type new_length, VALUE&& value)
569 {
570 const auto current_size = size();
571 if (new_length < current_size)
572 {
573 erase_values(new_length, current_size - new_length);
574 }
575 else if (new_length > current_size)
576 {
577 insert(cend(), std::forward<VALUE>(value), new_length - current_size);
578 }
579 }
580
582 {
583 return iterator(functor_type{&this->derived_cast()}, index);
584 }
585
591 std::vector<detail::union_rebuild_value>& entries,
592 size_type from,
593 size_type to
594 ) const
595 {
596 for (size_type index = from; index < to; ++index)
597 {
598 entries.push_back(detail::union_rebuild_value{
599 .child = m_type_id_map[p_type_ids[index]],
600 .row = this->derived_cast().element_offset(index)
601 });
602 }
603 }
604
612 const_iterator pos,
613 std::vector<array_traits::value_type> inserted,
614 size_type count
615 );
616
618
623 {
625 std::string format;
626
627 [[nodiscard]] bool operator==(const union_child_schema& other) const = default;
628 };
629
640 std::vector<union_child_schema> make_child_schemas(
641 std::vector<std::uint8_t>& child_type_ids,
642 std::array<bool, TYPE_ID_MAP_SIZE>& used_type_ids
643 ) const;
644
659 static std::vector<std::size_t> resolve_inserted_children(
660 std::span<const array_traits::value_type> inserted,
661 std::vector<union_child_schema>& child_schemas,
662 std::vector<std::uint8_t>& child_type_ids,
663 std::array<bool, TYPE_ID_MAP_SIZE>& used_type_ids,
664 std::vector<array>& new_child_templates
665 );
666
680 std::vector<detail::union_rebuild_value> make_union_fillers(
681 size_type old_child_count,
682 const std::vector<union_child_schema>& child_schemas,
683 std::vector<std::size_t>& inserted_children,
684 std::vector<array_traits::value_type>& inserted
685 ) const;
686
700 std::span<const detail::union_rebuild_value> values,
701 std::span<const std::size_t> inserted_children,
702 std::span<const std::uint8_t> child_type_ids,
703 std::span<const detail::union_rebuild_value> fillers
704 ) const;
705
717 std::vector<array> build_rebuilt_children(
718 const detail::union_rebuild_payload& payload,
719 std::span<const array_traits::value_type> inserted,
720 std::span<const std::size_t> inserted_children,
721 const children_type& old_children,
722 std::vector<array>& new_child_templates
723 ) const;
724
731 std::vector<array> children,
733 std::span<const std::uint8_t> child_type_ids
734 ) const;
735
741 arrow_proxy replacement,
742 std::vector<std::uint8_t> child_type_ids,
743 size_type return_index
744 );
745
749 std::vector<detail::union_rebuild_value> values,
750 std::vector<array_traits::value_type> inserted,
751 size_type return_index
752 );
753
761 [[nodiscard]] constexpr arrow_proxy& get_arrow_proxy();
762
770 [[nodiscard]] constexpr const arrow_proxy& get_arrow_proxy() const;
771
773 const std::uint8_t* p_type_ids;
775 std::vector<std::uint8_t> m_child_type_ids;
776 std::array<std::uint8_t, TYPE_ID_MAP_SIZE> m_type_id_map;
777
779
780#if defined(__cpp_lib_format)
781 friend struct std::formatter<DERIVED>;
782#endif
783 };
784
798 template <class D>
799 constexpr bool operator==(const union_array_crtp_base<D>& lhs, const union_array_crtp_base<D>& rhs);
800
832 class dense_union_array : public union_array_crtp_base<dense_union_array>
833 {
834 public:
835
839
854 template <class... Args>
856 explicit dense_union_array(Args&&... args)
857 : dense_union_array(create_proxy(std::forward<Args>(args)...))
858 {
859 }
860
873
884
897
900
901 private:
902
903 using type_id_map = typename base_type::type_id_map;
904
921 template <
922 std::ranges::input_range TYPE_MAPPING = std::vector<std::uint8_t>,
923 input_metadata_container METADATA_RANGE = std::vector<metadata_pair>>
924 requires(std::convertible_to<std::ranges::range_value_t<TYPE_MAPPING>, std::uint8_t>)
925 [[nodiscard]] static auto create_proxy(
926 std::vector<array>&& children,
927 type_id_buffer_type&& element_type,
928 offset_buffer_type&& offsets,
929 std::optional<TYPE_MAPPING>&& type_mapping = std::nullopt,
930 std::optional<std::string_view> name = std::nullopt,
931 std::optional<METADATA_RANGE> metadata = std::nullopt
932 ) -> arrow_proxy;
933
952 template <
953 std::ranges::input_range TYPE_ID_BUFFER_RANGE,
954 std::ranges::input_range OFFSET_BUFFER_RANGE,
955 std::ranges::input_range TYPE_MAPPING = std::vector<std::uint8_t>,
956 input_metadata_container METADATA_RANGE = std::vector<metadata_pair>>
957 requires(std::convertible_to<std::ranges::range_value_t<TYPE_MAPPING>, std::uint8_t>)
958 [[nodiscard]] static arrow_proxy create_proxy(
959 std::vector<array>&& children,
960 TYPE_ID_BUFFER_RANGE&& element_type,
961 OFFSET_BUFFER_RANGE&& offsets,
962 std::optional<TYPE_MAPPING>&& type_mapping = std::nullopt,
963 std::optional<std::string_view> name = std::nullopt,
964 std::optional<METADATA_RANGE> metadata = std::nullopt
965 )
966 {
967 SPARROW_ASSERT_TRUE(element_type.size() == offsets.size());
968 type_id_buffer_type element_type_buffer{std::move(element_type)};
969 offset_buffer_type offsets_buffer{std::move(offsets)};
970 return dense_union_array::create_proxy(
971 std::move(children),
972 std::move(element_type_buffer),
973 std::move(offsets_buffer),
974 std::move(type_mapping),
975 std::move(name),
976 std::move(metadata)
977 );
978 }
979
996 template <input_metadata_container METADATA_RANGE = std::vector<metadata_pair>>
997 [[nodiscard]] static arrow_proxy create_proxy_impl(
998 std::vector<array>&& children,
999 type_id_buffer_type&& element_type,
1000 offset_buffer_type&& offsets,
1001 std::string&& format,
1002 std::optional<std::string_view> name = std::nullopt,
1003 std::optional<METADATA_RANGE> metadata = std::nullopt
1004 );
1005
1022 template <
1023 std::ranges::input_range TYPE_ID_BUFFER_RANGE,
1024 std::ranges::input_range OFFSET_BUFFER_RANGE,
1025 input_metadata_container METADATA_RANGE = std::vector<metadata_pair>>
1026 [[nodiscard]] static arrow_proxy create_proxy_impl(
1027 std::vector<array>&& children,
1028 TYPE_ID_BUFFER_RANGE&& element_type,
1029 OFFSET_BUFFER_RANGE&& offsets,
1030 std::string&& format,
1031 std::optional<std::string_view> name = std::nullopt,
1032 std::optional<METADATA_RANGE> metadata = std::nullopt
1033 )
1034 {
1035 SPARROW_ASSERT_TRUE(std::ranges::distance(element_type) == std::ranges::distance(offsets));
1036 SPARROW_ASSERT_TRUE(std::ranges::distance(element_type) == children.size());
1037 type_id_buffer_type element_type_buffer{std::move(element_type)};
1038 offset_buffer_type offsets_buffer{std::move(offsets)};
1039 return dense_union_array::create_proxy_impl(
1040 std::move(children),
1041 std::move(element_type_buffer),
1042 std::move(offsets_buffer),
1043 std::move(format),
1044 std::move(name),
1045 std::move(metadata)
1046 );
1047 }
1048
1059 SPARROW_API std::size_t element_offset(std::size_t i) const;
1060
1061 SPARROW_API iterator rebuild_in_place(
1062 std::vector<detail::union_rebuild_value> values,
1063 std::vector<array_traits::value_type> inserted,
1064 size_type return_index
1065 );
1066
1067 const std::int32_t* p_offsets;
1069 };
1070
1099 class sparse_union_array : public union_array_crtp_base<sparse_union_array>
1100 {
1101 public:
1102
1105
1120 template <class... Args>
1122 explicit sparse_union_array(Args&&... args)
1123 : sparse_union_array(create_proxy(std::forward<Args>(args)...))
1124 {
1125 }
1126
1139
1142
1143 private:
1144
1145 using type_id_map = typename base_type::type_id_map;
1146
1162 template <
1163 std::ranges::input_range TYPE_MAPPING = std::vector<std::uint8_t>,
1164 input_metadata_container METADATA_RANGE = std::vector<metadata_pair>>
1165 requires(std::convertible_to<std::ranges::range_value_t<TYPE_MAPPING>, std::uint8_t>)
1166 static auto create_proxy(
1167 std::vector<array>&& children,
1168 type_id_buffer_type&& element_type,
1169 std::optional<TYPE_MAPPING>&& type_mapping = std::nullopt,
1170 std::optional<std::string_view> name = std::nullopt,
1171 std::optional<METADATA_RANGE> metadata = std::nullopt
1172 ) -> arrow_proxy;
1173
1189 template <input_metadata_container METADATA_RANGE>
1190 static auto create_proxy_impl(
1191 std::vector<array>&& children,
1192 type_id_buffer_type&& element_type,
1193 std::string&& format,
1194 std::optional<std::string_view> name = std::nullopt,
1195 std::optional<METADATA_RANGE> metadata = std::nullopt
1196 ) -> arrow_proxy;
1197
1211 [[nodiscard]] SPARROW_API std::size_t element_offset(std::size_t i) const;
1212
1213 SPARROW_API iterator rebuild_in_place(
1214 std::vector<detail::union_rebuild_value> values,
1215 std::vector<array_traits::value_type> inserted,
1216 size_type return_index
1217 );
1218
1220 };
1221
1222 /****************************************
1223 * union_array_crtp_base implementation *
1224 ****************************************/
1225
1226 template <class DERIVED>
1228 -> std::vector<std::uint8_t>
1229 {
1230 SPARROW_ASSERT_TRUE(format_string.starts_with("+ud:") || format_string.starts_with("+us:"));
1231 format_string.remove_prefix(4);
1232
1233 std::vector<std::uint8_t> result;
1234 while (!format_string.empty())
1235 {
1236 const auto separator = format_string.find(',');
1237 const auto token = format_string.substr(0, separator);
1238 std::uint32_t type_id = 0;
1239 const auto [end, error] = std::from_chars(token.data(), token.data() + token.size(), type_id);
1240 SPARROW_ASSERT_TRUE(error == std::errc{} && end == token.data() + token.size());
1241 SPARROW_ASSERT_TRUE(type_id <= std::numeric_limits<std::uint8_t>::max());
1242 result.push_back(static_cast<std::uint8_t>(type_id));
1243 if (separator == std::string_view::npos)
1244 {
1245 break;
1246 }
1247 format_string.remove_prefix(separator + 1);
1248 }
1249 return result;
1250 }
1251
1252 template <class DERIVED>
1253 constexpr auto
1254 union_array_crtp_base<DERIVED>::make_type_id_map(std::span<const std::uint8_t> child_type_ids)
1255 -> type_id_map
1256 {
1257 type_id_map result{};
1258 std::array<bool, TYPE_ID_MAP_SIZE> seen{};
1259 for (std::size_t child_index = 0; child_index < child_type_ids.size(); ++child_index)
1260 {
1261 const auto type_id = child_type_ids[child_index];
1263 SPARROW_ASSERT_TRUE(!seen[type_id]);
1264 seen[type_id] = true;
1265 result[type_id] = static_cast<std::uint8_t>(child_index);
1266 }
1267 return result;
1268 }
1269
1270 template <class DERIVED>
1271 template <std::ranges::input_range R>
1272 constexpr auto
1273 union_array_crtp_base<DERIVED>::type_id_map_from_child_to_type_id(const std::optional<R>& child_index_to_type_id)
1274 -> type_id_map
1275 {
1276 type_id_map ret{};
1277 if (!child_index_to_type_id.has_value())
1278 {
1279 constexpr std::array<std::uint8_t, TYPE_ID_MAP_SIZE> default_mapping = []
1280 {
1281 std::array<std::uint8_t, TYPE_ID_MAP_SIZE> arr{};
1282 std::iota(arr.begin(), arr.end(), 0);
1283 return arr;
1284 }();
1285 return default_mapping;
1286 }
1287 else
1288 {
1289 std::size_t child_index = 0;
1290 for (const auto type_id : *child_index_to_type_id)
1291 {
1293 const auto id = static_cast<std::size_t>(type_id);
1295 ret[id] = static_cast<std::uint8_t>(child_index++);
1296 }
1297 }
1298 return ret;
1299 }
1300
1301 template <class DERIVED>
1302 template <std::ranges::input_range R>
1303 requires(std::convertible_to<std::ranges::range_value_t<R>, std::uint8_t>)
1304 constexpr std::string
1305 union_array_crtp_base<DERIVED>::make_format_string(bool dense, const std::size_t n, const std::optional<R>& range)
1306 {
1307 const auto range_size = range.has_value() ? std::ranges::size(*range) : 0;
1308 if (range_size != n && range_size != 0)
1309 {
1310 throw std::invalid_argument("Invalid type-id map");
1311 }
1312
1313 std::string result = dense ? "+ud:" : "+us:";
1314 bool first = true;
1315 const auto append_type_id = [&result, &first](const auto type_id)
1316 {
1317 if (!first)
1318 {
1319 result.push_back(',');
1320 }
1321 result += std::to_string(type_id);
1322 first = false;
1323 };
1324
1325 if (range_size == 0)
1326 {
1327 for (const auto type_id : std::views::iota(std::size_t{0}, n))
1328 {
1329 append_type_id(type_id);
1330 }
1331 }
1332 else
1333 {
1334 for (const auto type_id : *range)
1335 {
1336 append_type_id(type_id);
1337 }
1338 }
1339 return result;
1340 }
1341
1342 template <class DERIVED>
1343 constexpr std::optional<std::string_view> union_array_crtp_base<DERIVED>::name() const
1344 {
1345 return m_proxy.name();
1346 }
1347
1348 template <class DERIVED>
1350 {
1351 return m_proxy.metadata();
1352 }
1353
1354 template <class DERIVED>
1359
1360 template <class DERIVED>
1362 {
1363 return m_proxy;
1364 }
1365
1366 template <class DERIVED>
1368 : m_proxy(std::move(proxy))
1369 , p_type_ids(reinterpret_cast<std::uint8_t*>(m_proxy.buffers()[0 /*index of type-ids*/].data()))
1373 {
1374 }
1375
1376 template <class DERIVED>
1381
1382 template <class DERIVED>
1384 {
1385 if (this != &rhs)
1386 {
1387 m_proxy = rhs.m_proxy;
1388 p_type_ids = reinterpret_cast<std::uint8_t*>(m_proxy.buffers()[0 /*index of type-ids*/].data());
1390 m_child_type_ids = rhs.m_child_type_ids;
1391 m_type_id_map = rhs.m_type_id_map;
1392 }
1393 return *this;
1394 }
1395
1396 template <class DERIVED>
1398 {
1399 const auto type_id = static_cast<std::size_t>(p_type_ids[i + m_proxy.offset()]);
1400 const auto child_index = m_type_id_map[type_id];
1401 const auto offset = this->derived_cast().element_offset(i);
1402 return array_element(*m_children[child_index], static_cast<std::size_t>(offset));
1403 }
1404
1405 template <class DERIVED>
1407 {
1408 return static_cast<const derived_type&>(*this)[i];
1409 }
1410
1411 template <class DERIVED>
1412 constexpr std::size_t union_array_crtp_base<DERIVED>::size() const
1413 {
1414 return m_proxy.length();
1415 }
1416
1417 template <class DERIVED>
1419 {
1420 return size() == 0;
1421 }
1422
1423 template <class DERIVED>
1425 {
1426 return iterator(functor_type{&(this->derived_cast())}, 0);
1427 }
1428
1429 template <class DERIVED>
1431 {
1432 return iterator(functor_type{&(this->derived_cast())}, this->size());
1433 }
1434
1435 template <class DERIVED>
1437 {
1438 return cbegin();
1439 }
1440
1441 template <class DERIVED>
1443 {
1444 return cend();
1445 }
1446
1447 template <class DERIVED>
1449 {
1450 return const_iterator(const_functor_type{&(this->derived_cast())}, 0);
1451 }
1452
1453 template <class DERIVED>
1455 {
1456 return const_iterator(const_functor_type{&(this->derived_cast())}, this->size());
1457 }
1458
1459 template <class DERIVED>
1461 {
1462 return const_reverse_iterator{cend()};
1463 }
1464
1465 template <class DERIVED>
1467 {
1469 }
1470
1471 template <class DERIVED>
1473 {
1474 return rbegin();
1475 }
1476
1477 template <class DERIVED>
1479 {
1480 return rend();
1481 }
1482
1483 template <class DERIVED>
1485 {
1486 return (*this)[0];
1487 }
1488
1489 template <class DERIVED>
1491 {
1492 return (*this)[this->size() - 1];
1493 }
1494
1495 template <class DERIVED>
1497 {
1498 children_type children(proxy.children().size(), nullptr);
1499 for (std::size_t i = 0; i < children.size(); ++i)
1500 {
1501 children[i] = array_factory(proxy.children()[i].view());
1502 }
1503 return children;
1504 }
1505
1506 template <class D>
1507 constexpr bool operator==(const union_array_crtp_base<D>& lhs, const union_array_crtp_base<D>& rhs)
1508 {
1509 return std::ranges::equal(lhs, rhs);
1510 }
1511
1512 template <class DERIVED>
1513 template <std::input_iterator InputIt>
1514 auto union_array_crtp_base<DERIVED>::insert(const_iterator pos, InputIt first, InputIt last, size_type count)
1515 -> iterator
1516 {
1517 // A no-op insertion must not require offset zero (see insert_materialized).
1518 if (count == 0)
1519 {
1520 return iterator_at(static_cast<size_type>(pos - cbegin()));
1521 }
1522 return insert_materialized(pos, std::vector<array_traits::value_type>(first, last), count);
1523 }
1524
1525 template <class DERIVED>
1530
1531 template <class DERIVED>
1533 {
1534 insert(cend(), value);
1535 }
1536
1537 template <class DERIVED>
1542
1543 template <class DERIVED>
1545 {
1546 resize_impl(new_length, value);
1547 }
1548
1549 template <class DERIVED>
1551 {
1552 resize_impl(new_length, value);
1553 }
1554
1555 template <class DERIVED>
1557 {
1558 const auto index = static_cast<size_type>(pos - cbegin());
1559 SPARROW_ASSERT_TRUE(index < size());
1560 return erase_values(index, 1);
1561 }
1562
1563 template <class DERIVED>
1565 {
1566 const auto first_index = static_cast<size_type>(first - cbegin());
1567 const auto last_index = static_cast<size_type>(last - cbegin());
1568 SPARROW_ASSERT_TRUE(first_index <= last_index);
1569 SPARROW_ASSERT_TRUE(last_index <= size());
1570 return erase_values(first_index, last_index - first_index);
1571 }
1572
1573 template <class DERIVED>
1575 std::vector<std::uint8_t>& child_type_ids,
1576 std::array<bool, TYPE_ID_MAP_SIZE>& used_type_ids
1577 ) const -> std::vector<union_child_schema>
1578 {
1579 const auto child_count = m_children.size();
1580 child_type_ids = m_child_type_ids;
1581 SPARROW_ASSERT_TRUE(child_type_ids.size() == child_count);
1582
1583 std::vector<union_child_schema> child_schemas;
1584 child_schemas.reserve(child_count);
1585 for (std::size_t child_index = 0; child_index < child_count; ++child_index)
1586 {
1587 used_type_ids[child_type_ids[child_index]] = true;
1588 child_schemas.emplace_back(
1589 m_children[child_index]->data_type(),
1590 std::string(m_children[child_index]->get_arrow_proxy().format())
1591 );
1592 }
1593 return child_schemas;
1594 }
1595
1596 template <class DERIVED>
1598 {
1599 using rebuild_value = detail::union_rebuild_value;
1600 SPARROW_ASSERT_TRUE(m_proxy.offset() == 0);
1601 const auto current_size = size();
1602 SPARROW_ASSERT_TRUE(first <= current_size);
1603 SPARROW_ASSERT_TRUE(count <= current_size - first);
1604 if (count == 0)
1605 {
1606 return iterator_at(first);
1607 }
1608
1609 std::vector<rebuild_value> values;
1610 values.reserve(current_size - count);
1611 this->append_rebuild_entries(values, 0, first);
1612 this->append_rebuild_entries(values, first + count, current_size);
1613 return this->derived_cast().rebuild_in_place(std::move(values), {}, first);
1614 }
1615
1617 {
1621
1622 // Defined in union_array.cpp: array.hpp includes this header, so the null child cannot be built here.
1623 [[nodiscard]] SPARROW_API array make_null_child(std::size_t count, const arrow_proxy& child_proxy);
1624
1626 inline std::size_t next_free_type_id(std::span<const bool> used_type_ids)
1627 {
1628 return static_cast<std::size_t>(std::ranges::find(used_type_ids, false) - used_type_ids.begin());
1629 }
1630
1633 inline std::size_t child_of(const rebuild_value& value, std::span<const std::size_t> inserted_children)
1634 {
1635 if (value.inserted)
1636 {
1637 SPARROW_ASSERT_TRUE(value.row < inserted_children.size());
1638 return inserted_children[value.row];
1639 }
1640 return value.child;
1641 }
1642
1644 {
1645 const array view{child.get_arrow_proxy().view()};
1646 return array_empty_like(view);
1647 }
1648
1656 template <class T>
1658 {
1659 using stored_type = std::remove_cvref_t<T>;
1660 return dynamic_value(nullable<stored_type>(stored_type(value), false));
1661 }
1662
1669 {
1670 return std::visit(
1671 [](const auto& typed_value) -> dynamic_value
1672 {
1673 return make_null_dynamic_value(typed_value.get());
1674 },
1675#if SPARROW_GCC_11_2_WORKAROUND
1676 static_cast<const dynamic_value::base_type&>(value)
1677#else
1678 value
1679#endif
1680 );
1681 }
1682
1691 std::span<const rebuild_value> values,
1692 std::span<const std::size_t> inserted_children,
1693 std::span<const std::uint8_t> child_type_ids
1694 )
1695 {
1696 union_rebuild_payload payload;
1697 payload.child_values.resize(child_type_ids.size());
1698 payload.type_ids.reserve(values.size());
1699 payload.offsets.reserve(values.size());
1700
1701 std::vector<std::size_t> child_value_counts(child_type_ids.size(), 0);
1702 for (const auto& value : values)
1703 {
1704 ++child_value_counts[child_of(value, inserted_children)];
1705 }
1706 for (std::size_t child_index = 0; child_index < child_type_ids.size(); ++child_index)
1707 {
1708 payload.child_values[child_index].reserve(child_value_counts[child_index]);
1709 }
1710
1711 for (const auto& value : values)
1712 {
1713 const auto child_index = child_of(value, inserted_children);
1714 payload.type_ids.push_back(child_type_ids[child_index]);
1716 payload.child_values[child_index].size()
1717 <= static_cast<std::size_t>(std::numeric_limits<std::int32_t>::max())
1718 );
1719 payload.offsets.push_back(static_cast<std::uint32_t>(payload.child_values[child_index].size()));
1720 payload.child_values[child_index].push_back(value);
1721 }
1722 return payload;
1723 }
1724
1734 std::span<const rebuild_value> values,
1735 std::span<const std::size_t> inserted_children,
1736 std::span<const std::uint8_t> child_type_ids,
1737 std::span<const rebuild_value> fillers
1738 )
1739 {
1740 union_rebuild_payload payload;
1741 payload.child_values.resize(child_type_ids.size());
1742 payload.type_ids.reserve(values.size());
1743 for (const auto& value : values)
1744 {
1745 payload.type_ids.push_back(child_type_ids[child_of(value, inserted_children)]);
1746 }
1747
1748 for (std::size_t child_index = 0; child_index < child_type_ids.size(); ++child_index)
1749 {
1750 SPARROW_ASSERT_TRUE(child_index < fillers.size());
1751 auto& child_values = payload.child_values[child_index];
1752 child_values.reserve(values.size());
1753 for (const auto& value : values)
1754 {
1755 child_values.push_back(
1756 child_of(value, inserted_children) == child_index ? value : fillers[child_index]
1757 );
1758 }
1759 }
1760 return payload;
1761 }
1762
1769 inline array make_filler_row(const array& old_child_view, std::size_t row)
1770 {
1771 array filler = old_child_view.slice(row, row + 1);
1772 auto& proxy = detail::array_access::get_arrow_proxy(filler);
1773 if (auto& bitmap = proxy.bitmap(); bitmap.has_value())
1774 {
1775 *bitmap->begin() = false;
1776 proxy.set_null_count(1);
1777 }
1778 return filler;
1779 }
1780
1784 inline void
1785 append_old_child_values(array& destination, const array& old_child_view, std::size_t first_row, std::size_t count)
1786 {
1787 const array rows = old_child_view.slice_view(first_row, first_row + count);
1788 destination.insert(destination.cend(), rows.cbegin(), rows.cend());
1789 }
1790
1798 template <typename CHILDREN>
1800 array& destination,
1801 std::span<const rebuild_value> values,
1802 std::span<const dynamic_value> inserted,
1803 std::span<const std::size_t> inserted_children,
1804 const CHILDREN& old_children
1805 )
1806 {
1807 // View over the old child the rows are read from, and the filler row reused by every
1808 // filler run of that child (interleaved filler rows would otherwise be re-sliced once
1809 // per row).
1810 std::optional<array> old_child_view;
1811 std::optional<array> filler_row;
1812
1813 for (std::size_t index = 0; index < values.size();)
1814 {
1815 const auto child_index = child_of(values[index], inserted_children);
1816 const bool is_inserted = values[index].inserted;
1817 const bool force_null = values[index].force_null;
1818 const auto row = values[index].row;
1819
1820 // A run repeats the same row (filler, inserted value) or reads consecutive rows.
1821 std::size_t count = 1;
1822 while (index + count < values.size())
1823 {
1824 const auto& next = values[index + count];
1825 if (child_of(next, inserted_children) != child_index || next.inserted != is_inserted
1826 || next.force_null != force_null
1827 || next.row != (force_null || is_inserted ? row : row + count))
1828 {
1829 break;
1830 }
1831 ++count;
1832 }
1833
1834 if (is_inserted)
1835 {
1836 const array inserted_row = array_make_from_element(inserted[row]);
1837 destination.insert(destination.cend(), inserted_row.cbegin(), inserted_row.cend(), count);
1838 index += count;
1839 continue;
1840 }
1841
1842 if (!old_child_view.has_value())
1843 {
1844 old_child_view = array{old_children[child_index]->get_arrow_proxy().view()};
1845 }
1846
1847 if (force_null)
1848 {
1849 if (!filler_row.has_value())
1850 {
1851 filler_row = make_filler_row(*old_child_view, row);
1852 }
1853 destination.insert(destination.cend(), filler_row->cbegin(), filler_row->cend(), count);
1854 }
1855 else
1856 {
1857 append_old_child_values(destination, *old_child_view, row, count);
1858 }
1859 index += count;
1860 }
1861 }
1862 }
1863
1864 template <class DERIVED>
1866 std::span<const array_traits::value_type> inserted,
1867 std::vector<union_child_schema>& child_schemas,
1868 std::vector<std::uint8_t>& child_type_ids,
1869 std::array<bool, TYPE_ID_MAP_SIZE>& used_type_ids,
1870 std::vector<array>& new_child_templates
1871 ) -> std::vector<std::size_t>
1872 {
1873 std::vector<std::size_t> inserted_children;
1874 inserted_children.reserve(inserted.size());
1875 for (const auto& value : inserted)
1876 {
1877 auto child = array_make_from_element(value);
1878 const union_child_schema schema{
1879 child.data_type(),
1880 std::string(detail::array_access::get_arrow_proxy(child).format())
1881 };
1882 const auto existing = std::ranges::find(child_schemas, schema);
1883 if (existing != child_schemas.end())
1884 {
1885 inserted_children.push_back(static_cast<std::size_t>(existing - child_schemas.begin()));
1886 continue;
1887 }
1888
1889 SPARROW_ASSERT_TRUE(child_schemas.size() < TYPE_ID_MAP_SIZE);
1890 const auto type_id = detail::union_rebuild::next_free_type_id(used_type_ids);
1892 used_type_ids[type_id] = true;
1893
1894 inserted_children.push_back(child_schemas.size());
1895 child_schemas.push_back(schema);
1896 child_type_ids.push_back(static_cast<std::uint8_t>(type_id));
1897 new_child_templates.push_back(std::move(child));
1898 }
1899 return inserted_children;
1900 }
1901
1902 template <class DERIVED>
1904 size_type old_child_count,
1905 const std::vector<union_child_schema>& child_schemas,
1906 std::vector<std::size_t>& inserted_children,
1907 std::vector<array_traits::value_type>& inserted
1908 ) const -> std::vector<detail::union_rebuild_value>
1909 {
1910 std::vector<detail::union_rebuild_value> fillers(child_schemas.size());
1911 for (std::size_t child_index = 0; child_index < fillers.size(); ++child_index)
1912 {
1913 if (child_index < old_child_count && array_size(*m_children[child_index]) > 0)
1914 {
1915 fillers[child_index] = detail::union_rebuild_value{
1916 .child = child_index,
1917 .row = 0,
1918 .force_null = true
1919 };
1920 continue;
1921 }
1922
1923 // A child created for an inserted value has no rows yet: derive its null from the
1924 // value that created it, an empty old child from its own default element.
1925 array_traits::value_type default_value;
1926 if (child_index < old_child_count)
1927 {
1928 default_value = array_default_value(*m_children[child_index]);
1929 }
1930 else
1931 {
1932 const auto creator = std::ranges::find(inserted_children, child_index);
1933 SPARROW_ASSERT_TRUE(creator != inserted_children.end());
1934 default_value = inserted[static_cast<std::size_t>(creator - inserted_children.begin())];
1935 }
1936 inserted.push_back(detail::union_rebuild::make_null_like(default_value));
1937 // Keep the two parallel lists in sync: entries refer to the filler by inserted value
1938 // index, and their child index is read back from inserted_children.
1939 inserted_children.push_back(child_index);
1940 fillers[child_index] = detail::union_rebuild_value{.row = inserted.size() - 1, .inserted = true};
1941 }
1942 return fillers;
1943 }
1944
1945 template <class DERIVED>
1947 std::span<const detail::union_rebuild_value> values,
1948 std::span<const std::size_t> inserted_children,
1949 std::span<const std::uint8_t> child_type_ids,
1950 std::span<const detail::union_rebuild_value> fillers
1952 {
1953 if constexpr (is_dense_union_array_v<DERIVED>)
1954 {
1955 return detail::union_rebuild::make_dense_rebuild_payload(values, inserted_children, child_type_ids);
1956 }
1957 else
1958 {
1960 values,
1961 inserted_children,
1962 child_type_ids,
1963 fillers
1964 );
1965 }
1966 }
1967
1968 template <class DERIVED>
1970 const detail::union_rebuild_payload& payload,
1971 std::span<const array_traits::value_type> inserted,
1972 std::span<const std::size_t> inserted_children,
1973 const children_type& old_children,
1974 std::vector<array>& new_child_templates
1975 ) const -> std::vector<array>
1976 {
1977 const auto old_child_count = old_children.size();
1978 std::vector<array> new_children;
1979 new_children.reserve(payload.child_values.size());
1980 for (std::size_t child_index = 0; child_index < payload.child_values.size(); ++child_index)
1981 {
1982 array child = child_index < old_child_count
1983 ? detail::union_rebuild::make_empty_child(*old_children[child_index])
1984 : std::move(new_child_templates[child_index - old_child_count]);
1985 auto& values_for_child = payload.child_values[child_index];
1986 if (child.data_type() == data_type::NA)
1987 {
1988 const auto& child_proxy = detail::array_access::get_arrow_proxy(child);
1989 child = detail::union_rebuild::make_null_child(values_for_child.size(), child_proxy);
1990 }
1991 else
1992 {
1993 child.erase(child.cbegin(), child.cend());
1995 child,
1996 values_for_child,
1997 inserted,
1998 inserted_children,
1999 old_children
2000 );
2001 }
2002 new_children.push_back(std::move(child));
2003 }
2004 return new_children;
2005 }
2006
2007 template <class DERIVED>
2009 std::vector<array> children,
2011 std::span<const std::uint8_t> child_type_ids
2012 ) const -> arrow_proxy
2013 {
2014 auto metadata = m_proxy.metadata();
2015 std::optional<std::vector<std::uint8_t>> type_id_mapping{
2016 std::vector<std::uint8_t>{child_type_ids.begin(), child_type_ids.end()}
2017 };
2018 if constexpr (is_dense_union_array_v<DERIVED>)
2019 {
2020 return DERIVED::create_proxy(
2021 std::move(children),
2022 std::move(payload.type_ids),
2023 std::move(payload.offsets),
2024 std::move(type_id_mapping),
2025 m_proxy.name(),
2026 std::move(metadata)
2027 );
2028 }
2029 else
2030 {
2031 return DERIVED::create_proxy(
2032 std::move(children),
2033 std::move(payload.type_ids),
2034 std::move(type_id_mapping),
2035 m_proxy.name(),
2036 std::move(metadata)
2037 );
2038 }
2039 }
2040
2041 template <class DERIVED>
2043 arrow_proxy replacement,
2044 std::vector<std::uint8_t> child_type_ids,
2045 size_type return_index
2046 ) -> iterator
2047 {
2048 m_proxy = std::move(replacement);
2049 p_type_ids = reinterpret_cast<std::uint8_t*>(m_proxy.buffers()[0].data());
2051 m_child_type_ids = std::move(child_type_ids);
2053 if constexpr (is_dense_union_array_v<DERIVED>)
2054 {
2055 this->derived_cast().p_offsets = reinterpret_cast<std::int32_t*>(m_proxy.buffers()[1].data());
2056 }
2057 return iterator(functor_type{&this->derived_cast()}, return_index);
2058 }
2059
2060 template <class DERIVED>
2062 std::vector<detail::union_rebuild_value> values,
2063 std::vector<array_traits::value_type> inserted,
2064 size_type return_index
2065 ) -> iterator
2066 {
2067 const auto old_child_count = m_children.size();
2068
2069 std::vector<std::uint8_t> child_type_ids;
2070 std::array<bool, TYPE_ID_MAP_SIZE> used_type_ids{};
2071 std::vector<union_child_schema> child_schemas = make_child_schemas(child_type_ids, used_type_ids);
2072
2073 // Inserted values are resolved once each, however often the plan refers to them.
2074 std::vector<array> new_child_templates;
2075 std::vector<std::size_t> inserted_children = resolve_inserted_children(
2076 inserted,
2077 child_schemas,
2078 child_type_ids,
2079 used_type_ids,
2080 new_child_templates
2081 );
2082
2083 std::vector<detail::union_rebuild_value> fillers;
2084 if constexpr (!is_dense_union_array_v<DERIVED>)
2085 {
2086 fillers = make_union_fillers(old_child_count, child_schemas, inserted_children, inserted);
2087 }
2088
2090 std::span<const detail::union_rebuild_value>{values},
2091 std::span<const std::size_t>{inserted_children},
2092 child_type_ids,
2093 std::span<const detail::union_rebuild_value>{fillers}
2094 );
2095
2096 std::vector<array> new_children = build_rebuilt_children(
2097 payload,
2098 inserted,
2099 inserted_children,
2100 m_children,
2101 new_child_templates
2102 );
2103
2104 auto replacement = create_rebuild_proxy(std::move(new_children), std::move(payload), child_type_ids);
2105 return adopt_rebuilt_array(std::move(replacement), std::move(child_type_ids), return_index);
2106 }
2107
2108 template <class DERIVED>
2110 -> iterator
2111 {
2112 // A no-op insertion must not require offset zero (see insert_materialized).
2113 if (count == 0)
2114 {
2115 return iterator_at(static_cast<size_type>(pos - cbegin()));
2116 }
2117 return insert(pos, array_traits::value_type(value), count);
2118 }
2119
2120 template <class DERIVED>
2122 const_iterator pos,
2123 const array_traits::value_type& value,
2124 size_type count
2125 ) -> iterator
2126 {
2127 // A no-op insertion must not require offset zero (see insert_materialized).
2128 if (count == 0)
2129 {
2130 return iterator_at(static_cast<size_type>(pos - cbegin()));
2131 }
2132 return insert_materialized(pos, std::vector<array_traits::value_type>{value}, count);
2133 }
2134
2135 template <class DERIVED>
2137 const_iterator pos,
2138 std::vector<array_traits::value_type> inserted,
2139 size_type count
2140 ) -> iterator
2141 {
2142 const auto pos_index = static_cast<size_type>(pos - cbegin());
2143 if (inserted.empty())
2144 {
2145 return iterator_at(pos_index);
2146 }
2147
2148 SPARROW_ASSERT_TRUE(count > 0);
2149 SPARROW_ASSERT_TRUE(m_proxy.offset() == 0);
2150 SPARROW_ASSERT_TRUE(pos_index <= size());
2151
2152 const auto old_size = size();
2153 const auto value_count = static_cast<size_type>(inserted.size());
2154 std::vector<detail::union_rebuild_value> entries;
2155 entries.reserve(old_size + value_count * count);
2156
2157 append_rebuild_entries(entries, 0, pos_index);
2158 for (size_type repetition = 0; repetition < count; ++repetition)
2159 {
2160 for (size_type value_index = 0; value_index < value_count; ++value_index)
2161 {
2162 entries.push_back(detail::union_rebuild_value{
2163 .row = value_index,
2164 .inserted = true
2165 });
2166 }
2167 }
2168 append_rebuild_entries(entries, pos_index, old_size);
2169 return this->derived_cast().rebuild_in_place(std::move(entries), std::move(inserted), pos_index);
2170 }
2171
2172 /************************************
2173 * Union array shared implementation *
2174 ************************************/
2175
2176 namespace detail
2177 {
2178 template <input_metadata_container METADATA_RANGE = std::vector<metadata_pair>>
2180 std::vector<array>&& children,
2181 std::vector<buffer<std::uint8_t>>&& buffers,
2182 std::size_t size,
2183 std::string&& format,
2184 std::optional<std::string_view> name,
2185 std::optional<METADATA_RANGE> metadata
2186 )
2187 {
2188 const auto n_children = children.size();
2189 ArrowSchema** child_schemas = n_children == 0 ? nullptr : new ArrowSchema*[n_children];
2190 ArrowArray** child_arrays = n_children == 0 ? nullptr : new ArrowArray*[n_children];
2191
2192 for (std::size_t i = 0; i < n_children; ++i)
2193 {
2194 auto& child = children[i];
2195 auto [flat_arr, flat_schema] = extract_arrow_structures(std::move(child));
2196 child_arrays[i] = new ArrowArray(std::move(flat_arr));
2197 child_schemas[i] = new ArrowSchema(std::move(flat_schema));
2198 }
2199
2200 const bool is_nullable = n_children == 0
2201 || std::all_of(
2202 child_schemas,
2203 child_schemas + n_children,
2204 [](const ArrowSchema* schema)
2205 {
2206 return to_set_of_ArrowFlags(schema->flags).contains(
2208 );
2209 }
2210 );
2211
2212 const std::optional<std::unordered_set<sparrow::ArrowFlag>>
2213 flags = is_nullable
2214 ? std::make_optional(std::unordered_set<sparrow::ArrowFlag>{ArrowFlag::NULLABLE})
2215 : std::nullopt;
2216
2218 std::move(format),
2219 std::move(name), // name
2220 std::move(metadata), // metadata
2221 flags, // flags,
2222 child_schemas, // children
2223 repeat_view<bool>(true, n_children), // children_ownership
2224 nullptr, // dictionary,
2225 true // dictionary ownership
2226 );
2227
2229 static_cast<std::int64_t>(size), // length
2230 0, // null_count: always 0 as the nullability is in children
2231 0, // offset
2232 std::move(buffers),
2233 child_arrays, // children
2234 repeat_view<bool>(true, n_children), // children_ownership
2235 nullptr, // dictionary
2236 true
2237 );
2238
2239 return arrow_proxy{std::move(arr), std::move(schema)};
2240 }
2241 }
2242
2243 /************************************
2244 * dense_union_array implementation *
2245 ************************************/
2246
2247 template <std::ranges::input_range TYPE_MAPPING, input_metadata_container METADATA_RANGE>
2248 requires(std::convertible_to<std::ranges::range_value_t<TYPE_MAPPING>, std::uint8_t>)
2249 auto dense_union_array::create_proxy(
2250 std::vector<array>&& children,
2251 type_id_buffer_type&& element_type,
2252 offset_buffer_type&& offsets,
2253 std::optional<TYPE_MAPPING>&& child_index_to_type_id,
2254 std::optional<std::string_view> name,
2255 std::optional<METADATA_RANGE> metadata
2256 ) -> arrow_proxy
2257 {
2258 SPARROW_ASSERT_TRUE(element_type.size() == offsets.size());
2259 const auto n_children = children.size();
2260
2261 std::string format = make_format_string(true /*dense union*/, n_children, child_index_to_type_id);
2262
2263 return create_proxy_impl(
2264 std::move(children),
2265 std::move(element_type),
2266 std::move(offsets),
2267 std::move(format),
2268 std::move(name),
2269 std::move(metadata)
2270 );
2271 }
2272
2273 template <input_metadata_container METADATA_RANGE>
2274 auto dense_union_array::create_proxy_impl(
2275 std::vector<array>&& children,
2276 type_id_buffer_type&& element_type,
2277 offset_buffer_type&& offsets,
2278 std::string&& format,
2279 std::optional<std::string_view> name,
2280 std::optional<METADATA_RANGE> metadata
2281 ) -> arrow_proxy
2282 {
2283 SPARROW_ASSERT_TRUE(element_type.size() == offsets.size());
2284 const auto size = element_type.size();
2285
2286 std::vector<buffer<std::uint8_t>> arr_buffs;
2287 arr_buffs.reserve(2);
2288 arr_buffs.emplace_back(std::move(element_type).extract_storage());
2289 arr_buffs.emplace_back(std::move(offsets).extract_storage());
2290
2292 std::move(children),
2293 std::move(arr_buffs),
2294 size,
2295 std::move(format),
2296 std::move(name),
2297 std::move(metadata)
2298 );
2299 }
2300
2301 /*************************************
2302 * sparse_union_array implementation *
2303 *************************************/
2304
2305 template <std::ranges::input_range TYPE_MAPPING, input_metadata_container METADATA_RANGE>
2306 requires(std::convertible_to<std::ranges::range_value_t<TYPE_MAPPING>, std::uint8_t>)
2307 auto sparse_union_array::create_proxy(
2308 std::vector<array>&& children,
2309 type_id_buffer_type&& element_type,
2310 std::optional<TYPE_MAPPING>&& child_index_to_type_id,
2311 std::optional<std::string_view> name,
2312 std::optional<METADATA_RANGE> metadata
2313 ) -> arrow_proxy
2314 {
2315 const auto n_children = children.size();
2316 if (child_index_to_type_id.has_value())
2317 {
2318 SPARROW_ASSERT_TRUE((*child_index_to_type_id).size() == n_children);
2319 }
2320
2321 std::string format = make_format_string(false /*is dense union*/, n_children, child_index_to_type_id);
2322
2323 return create_proxy_impl(
2324 std::move(children),
2325 std::move(element_type),
2326 std::move(format),
2327 std::move(name),
2328 std::move(metadata)
2329 );
2330 }
2331
2332 template <input_metadata_container METADATA_RANGE>
2333 auto sparse_union_array::create_proxy_impl(
2334 std::vector<array>&& children,
2335 type_id_buffer_type&& element_type,
2336 std::string&& format,
2337 std::optional<std::string_view> name,
2338 std::optional<METADATA_RANGE> metadata
2339 ) -> arrow_proxy
2340 {
2341 for (const auto& child : children)
2342 {
2343 SPARROW_ASSERT_TRUE(child.size() == element_type.size());
2344 }
2345 const auto size = element_type.size();
2346
2347 std::vector<buffer<std::uint8_t>> arr_buffs;
2348 arr_buffs.reserve(1);
2349 arr_buffs.emplace_back(std::move(element_type).extract_storage());
2350
2352 std::move(children),
2353 std::move(arr_buffs),
2354 size,
2355 std::move(format),
2356 std::move(name),
2357 std::move(metadata)
2358 );
2359 }
2360}
2361
2362#if defined(__cpp_lib_format)
2363
2372template <typename U>
2373 requires std::derived_from<U, sparrow::union_array_crtp_base<U>>
2374struct std::formatter<U>
2375{
2382 constexpr auto parse(std::format_parse_context& ctx)
2383 {
2384 return ctx.begin(); // Simple implementation
2385 }
2386
2397 auto format(const U& ar, std::format_context& ctx) const
2398 {
2399 if constexpr (std::is_same_v<U, sparrow::dense_union_array>)
2400 {
2401 std::format_to(ctx.out(), "DenseUnion");
2402 }
2403 else if constexpr (std::is_same_v<U, sparrow::sparse_union_array>)
2404 {
2405 std::format_to(ctx.out(), "SparseUnion");
2406 }
2407 else
2408 {
2409 static_assert(sparrow::mpl::dependent_false<U>::value, "Unknown union array type");
2411 }
2412 const auto& proxy = ar.get_arrow_proxy();
2413 std::format_to(ctx.out(), " [name={} | size={}] <", proxy.name().value_or("nullptr"), proxy.length());
2414
2415 std::for_each(
2416 ar.cbegin(),
2417 std::prev(ar.cend()),
2418 [&ctx](const auto& value)
2419 {
2420 std::format_to(ctx.out(), "{}, ", value);
2421 }
2422 );
2423
2424 return std::format_to(ctx.out(), "{}>", ar.back());
2425 }
2426};
2427
2428namespace sparrow
2429{
2440 template <typename U>
2441 requires std::derived_from<U, union_array_crtp_base<U>>
2442 std::ostream& operator<<(std::ostream& os, const U& value)
2443 {
2444 os << std::format("{}", value);
2445 return os;
2446 }
2447}
2448
2449#endif
Base class for array type erasure.
constexpr arrow_proxy & get_arrow_proxy()
Dynamically typed array encapsulating an Arrow layout.
Definition array_api.hpp:50
SPARROW_API iterator erase(const_iterator pos)
Inserts a copy of value before pos in the array, repeating the insertion count times.
SPARROW_API array slice(size_type start, size_type end) const
Returns a deep-copy of the array restricted to the elements in the half-open range [start,...
SPARROW_API const_iterator cend() const
Returns a constant iterator to the end of the array.
SPARROW_API enum data_type data_type() const
SPARROW_API iterator insert(const_iterator pos, const_iterator first, const_iterator last)
Inserts elements from the range [first, last) before the element at the specified position.
SPARROW_API const_iterator cbegin() const
Returns a constant iterator to the beginning of the array.
SPARROW_API array slice_view(size_type start, size_type end) const
Returns a zero-copy view of the array restricted to the elements in the half-open range [start,...
SPARROW_API arrow_proxy view() const
Get a non-owning view of the arrow_proxy.
constexpr void push_back(const value_type &value)
Adds an element to the end.
constexpr void reserve(size_type new_cap)
Reserves storage for at least the specified number of elements.
Object that owns a piece of contiguous memory.
Definition buffer.hpp:142
Base class for CRTP base classes.
Definition crtp_base.hpp:29
constexpr derived_type & derived_cast()
Definition crtp_base.hpp:39
Dense union array implementation with offset buffer.
SPARROW_API dense_union_array & operator=(const dense_union_array &rhs)
Copy assignment operator.
u8_buffer< std::uint32_t > offset_buffer_type
union_array_crtp_base< dense_union_array > base_type
typename base_type::type_id_buffer_type type_id_buffer_type
dense_union_array(Args &&... args)
Generic constructor for creating dense union arrays.
SPARROW_API dense_union_array(arrow_proxy proxy)
Constructs dense union array from Arrow proxy.
SPARROW_API dense_union_array(const dense_union_array &rhs)
Copy constructor.
dense_union_array(dense_union_array &&rhs)=default
dense_union_array & operator=(dense_union_array &&rhs)=default
static const sparrow::arrow_proxy & get_arrow_proxy(const ARRAY &array)
A view that repeats a value a given number of times.
Sparse union array implementation without offset buffer.
SPARROW_API sparse_union_array & operator=(const sparse_union_array &)
sparse_union_array(Args &&... args)
Generic constructor for creating sparse union arrays.
union_array_crtp_base< sparse_union_array > base_type
typename base_type::type_id_buffer_type type_id_buffer_type
SPARROW_API sparse_union_array(const sparse_union_array &)
SPARROW_API sparse_union_array(arrow_proxy proxy)
Constructs sparse union array from Arrow proxy.
This buffer class is used as storage buffer for all sparrow arrays.
CRTP base class providing shared functionality for union array implementations.
array_traits::inner_value_type inner_value_type
std::reverse_iterator< const_iterator > const_reverse_iterator
static std::vector< std::uint8_t > child_type_ids_from_format(std::string_view format_string)
Extracts child type IDs from an Arrow union format string.
constexpr iterator begin()
Gets iterator to the beginning of the array.
detail::layout_bracket_functor< const derived_type, value_type > const_functor_type
constexpr std::optional< std::string_view > name() const
Gets the optional name of the union array.
iterator insert_materialized(const_iterator pos, std::vector< array_traits::value_type > inserted, size_type count)
Rebuilds the array with the owned inserted values repeated count times before pos.
array_traits::const_reference value_type
void resize(size_type new_length)
Resizes the array, default-inserting any appended values.
void push_back(const array_traits::value_type &value)
Appends an owned value.
iterator iterator_at(size_type index)
constexpr union_array_crtp_base(const self_type &rhs)
Copy constructor.
u8_buffer< std::uint8_t > type_id_buffer_type
iterator insert(const_iterator pos, const array_traits::value_type &value, size_type count=1)
Inserts copies of an owned value before pos.
static constexpr std::string make_format_string(bool dense, std::size_t n, const std::optional< R > &child_index_to_type_id)
Creates Arrow format string for union arrays.
SPARROW_CONSTEXPR_CLANG value_type front() const
Gets reference to the first element.
iterator rebuild_values(std::vector< detail::union_rebuild_value > values, std::vector< array_traits::value_type > inserted, size_type return_index)
Rebuilds this array from values, the inserted values they reference, and.
constexpr const_reverse_iterator crbegin() const
Gets const reverse iterator to the beginning of reversed array.
void resize(size_type new_length, const array_traits::value_type &value)
Resizes the array, inserting value when it grows.
std::array< std::uint8_t, TYPE_ID_MAP_SIZE > m_type_id_map
constexpr arrow_proxy & get_arrow_proxy()
Gets mutable reference to the Arrow proxy.
std::vector< union_child_schema > make_child_schemas(std::vector< std::uint8_t > &child_type_ids, std::array< bool, TYPE_ID_MAP_SIZE > &used_type_ids) const
Initializes the schema bookkeeping of a rebuild from the current children.
static std::vector< std::size_t > resolve_inserted_children(std::span< const array_traits::value_type > inserted, std::vector< union_child_schema > &child_schemas, std::vector< std::uint8_t > &child_type_ids, std::array< bool, TYPE_ID_MAP_SIZE > &used_type_ids, std::vector< array > &new_child_templates)
Maps each inserted value to the child it belongs to.
union_array_crtp_base< DERIVED > self_type
constexpr self_type & operator=(self_type &&rhs)=default
union_array_crtp_base(arrow_proxy proxy)
Protected constructor from Arrow proxy.
void resize_impl(size_type new_length, VALUE &&value)
array_traits::const_reference const_reference
detail::layout_bracket_functor< derived_type, value_type > functor_type
SPARROW_CONSTEXPR_CLANG value_type operator[](size_type i) const
Gets element at specified position without bounds checking.
std::array< std::uint8_t, TYPE_ID_MAP_SIZE > type_id_map
iterator insert(const_iterator pos, const_reference value, size_type count=1)
Inserts copies of a value before pos.
void resize(size_type new_length, const_reference value)
Resizes the array, inserting value when it grows.
iterator adopt_rebuilt_array(arrow_proxy replacement, std::vector< std::uint8_t > child_type_ids, size_type return_index)
Replaces this array's data with the rebuilt replacement.
iterator erase(const_iterator pos)
Erases the value at pos.
std::vector< detail::union_rebuild_value > make_union_fillers(size_type old_child_count, const std::vector< union_child_schema > &child_schemas, std::vector< std::size_t > &inserted_children, std::vector< array_traits::value_type > &inserted) const
Builds the row padding the output rows a child is not selected for.
functor_index_iterator< const_functor_type > const_iterator
static constexpr type_id_map type_id_map_from_child_to_type_id(const std::optional< R > &child_index_to_type_id)
Creates type ID mapping from child index to type ID mapping.
iterator insert(const_iterator pos, InputIt first, InputIt last, size_type count=1)
Inserts a range before pos, repeated count times.
constexpr const_reverse_iterator rbegin() const
Gets reverse iterator to the beginning of reversed array.
iterator erase_values(size_type first, size_type count)
constexpr const_iterator begin() const
Gets const iterator to the beginning of the array.
SPARROW_CONSTEXPR_CLANG std::optional< key_value_view > metadata() const
Gets the metadata associated with the union array.
functor_index_iterator< functor_type > iterator
constexpr self_type & operator=(const self_type &rhs)
Copy assignment operator.
std::vector< array > build_rebuilt_children(const detail::union_rebuild_payload &payload, std::span< const array_traits::value_type > inserted, std::span< const std::size_t > inserted_children, const children_type &old_children, std::vector< array > &new_child_templates) const
Rebuilds each child array from the payload rows planned for it.
constexpr bool empty() const
Checks if the union array is empty.
SPARROW_CONSTEXPR_CLANG value_type at(size_type i) const
Gets element at specified position with bounds checking.
constexpr const_iterator cbegin() const
Gets const iterator to the beginning of the array.
constexpr const_reverse_iterator crend() const
Gets const reverse iterator to the end of reversed array.
void append_rebuild_entries(std::vector< detail::union_rebuild_value > &entries, size_type from, size_type to) const
Appends one rebuild entry per element in [from, to), each referring to its current child slot.
static constexpr type_id_map make_type_id_map(std::span< const std::uint8_t > child_type_ids)
Creates a type-ID lookup table from child type IDs.
constexpr const_iterator end() const
Gets const iterator to the end of the array.
arrow_proxy create_rebuild_proxy(std::vector< array > children, detail::union_rebuild_payload &&payload, std::span< const std::uint8_t > child_type_ids) const
Creates the Arrow proxy of a rebuilt array from its children and buffers.
SPARROW_CONSTEXPR_CLANG value_type operator[](size_type i)
Gets mutable element at specified position.
constexpr const_iterator cend() const
Gets const iterator to the end of the array.
SPARROW_CONSTEXPR_CLANG value_type back() const
Gets reference to the last element.
constexpr union_array_crtp_base(self_type &&rhs)=default
void push_back(const_reference value)
Appends a borrowed value.
constexpr const arrow_proxy & get_arrow_proxy() const
Gets const reference to the Arrow proxy.
iterator erase(const_iterator first, const_iterator last)
Erases the half-open range [first, last).
constexpr iterator end()
Gets iterator to the end of the array.
constexpr size_type size() const
Gets the number of elements in the union array.
constexpr const_reverse_iterator rend() const
Gets reverse iterator to the end of reversed array.
constexpr void zero_null_values(const inner_value_type &value)
Replaces stored values at null positions.
std::vector< cloning_ptr< array_wrapper > > children_type
detail::union_rebuild_payload make_rebuild_payload(std::span< const detail::union_rebuild_value > values, std::span< const std::size_t > inserted_children, std::span< const std::uint8_t > child_type_ids, std::span< const detail::union_rebuild_value > fillers) const
Plans the rebuilt data from the row plan and the inserted-value resolution.
constexpr children_type make_children(arrow_proxy &proxy)
Creates child array wrappers from Arrow proxy.
Concept for input containers that can provide metadata pairs.
Definition metadata.hpp:332
#define SPARROW_CONSTEXPR_CLANG
Definition config.hpp:64
#define SPARROW_API
Definition config.hpp:38
#define SPARROW_ASSERT_TRUE(expr__)
union_rebuild_payload make_sparse_rebuild_payload(std::span< const rebuild_value > values, std::span< const std::size_t > inserted_children, std::span< const std::uint8_t > child_type_ids, std::span< const rebuild_value > fillers)
Creates a rebuild payload for a sparse union array.
void append_old_child_values(array &destination, const array &old_child_view, std::size_t first_row, std::size_t count)
Appends count consecutive rows of an old child, in a single insertion.
std::size_t child_of(const rebuild_value &value, std::span< const std::size_t > inserted_children)
Resolves the child a plan entry targets: inserted values carry no child index, only the index of the ...
dynamic_value make_null_dynamic_value(const T &value)
Builds a null dynamic value holding the type of the given inner value.
SPARROW_API array make_null_child(std::size_t count, const arrow_proxy &child_proxy)
detail::union_rebuild_payload union_rebuild_payload
union_rebuild_payload make_dense_rebuild_payload(std::span< const rebuild_value > values, std::span< const std::size_t > inserted_children, std::span< const std::uint8_t > child_type_ids)
Creates a rebuild payload for a dense union array.
std::size_t next_free_type_id(std::span< const bool > used_type_ids)
Finds an existing type ID not yet assigned, or TYPE_ID_MAP_SIZE if none is free.
void append_rebuild_values(array &destination, std::span< const rebuild_value > values, std::span< const dynamic_value > inserted, std::span< const std::size_t > inserted_children, const CHILDREN &old_children)
Appends the rebuild rows of one child to destination.
detail::union_rebuild_value rebuild_value
array make_filler_row(const array &old_child_view, std::size_t row)
Slices the row a filler rebuild value refers to, marked as null.
array_traits::value_type dynamic_value
array make_empty_child(const array_wrapper &child)
dynamic_value make_null_like(const dynamic_value &value)
Creates a null-like dynamic value for the given dynamic value.
constexpr std::size_t union_no_child
arrow_proxy create_union_proxy_impl(std::vector< array > &&children, std::vector< buffer< std::uint8_t > > &&buffers, std::size_t size, std::string &&format, std::optional< std::string_view > name, std::optional< METADATA_RANGE > metadata)
constexpr std::size_t size(typelist< T... >={})
Gets the count of types contained in a typelist.
Definition mp_utils.hpp:216
constexpr bool excludes_copy_and_move_ctor_v
Convenience variable template for excludes_copy_and_move_ctor.
void unreachable()
Invokes undefined behavior for optimization purposes.
Definition mp_utils.hpp:882
constexpr bool is_dense_union_array_v
Type trait to check if a type is a dense_union_array.
ArrowSchema make_arrow_schema(F format, N name, std::optional< M > metadata, std::optional< std::unordered_set< ArrowFlag > > flags, ArrowSchema **children, const CHILDREN_OWNERSHIP &children_ownership, ArrowSchema *dictionary, bool dictionary_ownership)
Creates an ArrowSchema owned by a unique_ptr and holding the provided data.
constexpr void zero_null_values(R &range, const T &default_value=T{})
Sets null values in a range to a default value.
SPARROW_API bool operator==(const array &lhs, const array &rhs)
Compares the content of two arrays.
SPARROW_API std::size_t array_size(const array_wrapper &ar)
SPARROW_API array_traits::value_type array_default_value(const array_wrapper &ar)
constexpr InputIt next(InputIt it, Distance n)
Definition iterator.hpp:605
SPARROW_API array array_make_from_element(const array_traits::value_type &value)
constexpr std::size_t range_size(R &&r)
Definition ranges.hpp:35
SPARROW_API array array_empty_like(const array &source)
std::pair< ArrowArray, ArrowSchema > extract_arrow_structures(A &&a)
Extracts the internal ArrowArray and ArrowSchema structures from the given array or typed layout.
Definition array.hpp:110
ArrowArray make_arrow_array(int64_t length, int64_t null_count, int64_t offset, B buffers, ArrowArray **children, const CHILDREN_OWNERSHIP &children_ownership, ArrowArray *dictionary, bool dictionary_ownership)
Creates an ArrowArray.
std::ostream & operator<<(std::ostream &os, const nullval_t &)
SPARROW_API cloning_ptr< array_wrapper > array_factory(arrow_proxy proxy)
SPARROW_API array_traits::const_reference array_element(const array_wrapper &ar, std::size_t index)
constexpr bool is_sparse_union_array_v
Type trait to check if a type is a sparse_union_array.
data_type
Runtime identifier of arrow data types, usually associated with raw bytes with the associated value.
std::unordered_set< ArrowFlag > to_set_of_ArrowFlags(int64_t flag_values)
Converts a bitfield of ArrowFlag values to a set of ArrowFlag values.
Extensions to the C++ standard library.
int64_t flags
mpl::rename< mpl::unique< mpl::transform< detail::array_const_reference_t, all_base_types_t > >, nullable_variant > const_reference
mpl::rename< mpl::transform< detail::array_value_type_t, all_base_types_t >, nullable_variant > value_type
mpl::rename< all_base_types_t, std::variant > inner_value_type
Metafunction for retrieving the data_type of a typed array.
Rebuilt data of a union array: the row plan of each child, plus the new type-ID and (dense-only) offs...
u8_buffer< std::uint32_t > offsets
Dense offsets (empty for sparse).
u8_buffer< std::uint8_t > type_ids
Type ID per output row.
std::vector< std::vector< union_rebuild_value > > child_values
Row plan of each child.
One row of a rebuild plan.
std::size_t child
Source child, or union_no_child when inserted.
bool inserted
True when the entry is an inserted value.
std::size_t row
Row of the source child, or inserted-value index.
bool force_null
Copy the row as null (sparse union filler row).
Schema of one child of a rebuilt union array.
bool operator==(const union_child_schema &other) const =default