sparrow 2.3.0
C++20 idiomatic APIs for the Apache Arrow Columnar Format
Loading...
Searching...
No Matches
variable_size_binary_view_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 <cstddef>
18#include <cstring>
19#include <ranges>
20#include <unordered_map>
21
31#include "sparrow/u8_buffer.hpp"
40
41namespace sparrow
42{
43 template <std::ranges::sized_range T, class CR, typename Ext = empty_extension>
45
46 namespace copy_tracker
47 {
48 template <typename T>
50 std::string key()
51 {
52 return "variable_size_binary_view_array";
53 }
54 }
55
66
76 arrow_traits<std::vector<byte_t>>::const_reference>;
77
78 namespace detail
79 {
80 template <>
82 {
83 [[nodiscard]] static constexpr sparrow::data_type get()
84 {
86 }
87 };
88
89 template <>
91 {
92 [[nodiscard]] static constexpr sparrow::data_type get()
93 {
95 }
96 };
97 }
98
99 template <std::ranges::sized_range T, class CR, typename Ext>
112
113 template <class T>
115 {
116 };
117
118 template <std::ranges::sized_range T, class CR, typename Ext>
120 : std::true_type
121 {
122 };
123
127 template <class T>
129
169 template <std::ranges::sized_range T, class CR, typename Ext>
171 : public mutable_array_bitmap_base<variable_size_binary_view_array_impl<T, CR, Ext>>,
172 public Ext
173 {
174 public:
175
178
180 using inner_value_type = typename inner_types::inner_value_type;
181 using inner_reference = typename inner_types::inner_reference;
182 using inner_const_reference = typename inner_types::inner_const_reference;
183
185 using bitmap_reference = typename base_type::bitmap_reference;
189 using bitmap_range = typename base_type::bitmap_range;
191
195
199
200 using value_iterator = typename base_type::value_iterator;
201 using const_value_iterator = typename base_type::const_value_iterator;
202
203 using iterator = typename base_type::iterator;
204 using const_iterator = typename base_type::const_iterator;
205
218
220 self_type& operator=(const self_type&) = default;
221
236 template <class... Args>
239 : variable_size_binary_view_array_impl(create_proxy(std::forward<Args>(args)...))
240 {
241 }
242
243 private:
244
253 struct buffers_collection
254 {
255 buffer<uint8_t> length_buffer;
256 buffer<uint8_t> long_string_storage;
257 u8_buffer<int64_t> buffer_sizes;
258 };
259
265 [[nodiscard]] static constexpr std::string_view get_arrow_format()
266 {
267 return std::is_same_v<T, arrow_traits<std::string>::value_type> ? std::string_view("vu")
268 : std::string_view("vz");
269 }
270
280 template <input_metadata_container METADATA_RANGE>
281 [[nodiscard]] static ArrowSchema create_arrow_schema(
282 std::optional<std::string_view> name,
283 std::optional<METADATA_RANGE> metadata,
284 std::optional<std::unordered_set<sparrow::ArrowFlag>> flags
285 )
286 {
287 constexpr repeat_view<bool> children_ownership(true, 0);
288 return make_arrow_schema(
289 get_arrow_format(),
290 std::move(name),
291 std::move(metadata),
292 flags,
293 nullptr, // children
294 children_ownership,
295 nullptr, // dictionary
296 true
297 );
298 }
299
318 template <std::ranges::input_range R>
319 requires std::convertible_to<std::ranges::range_value_t<R>, T>
320 static buffers_collection create_buffers(R&& range);
321
341 template <
342 std::ranges::input_range R,
343 validity_bitmap_input VB = validity_bitmap,
344 input_metadata_container METADATA_RANGE = std::vector<metadata_pair>>
345 requires std::convertible_to<std::ranges::range_value_t<R>, T>
346 [[nodiscard]] static arrow_proxy create_proxy(
347 R&& range,
349 std::optional<std::string_view> name = std::nullopt,
350 std::optional<METADATA_RANGE> metadata = std::nullopt
351 );
352
370 template <std::ranges::input_range NULLABLE_RANGE, input_metadata_container METADATA_RANGE = std::vector<metadata_pair>>
371 requires std::convertible_to<std::ranges::range_value_t<NULLABLE_RANGE>, nullable<T>>
372 [[nodiscard]] static arrow_proxy create_proxy(
373 NULLABLE_RANGE&& nullable_range,
374 std::optional<std::string_view> name = std::nullopt,
375 std::optional<METADATA_RANGE> metadata = std::nullopt
376 );
377
394 template <std::ranges::input_range R, input_metadata_container METADATA_RANGE = std::vector<metadata_pair>>
395 requires std::convertible_to<std::ranges::range_value_t<R>, T>
396 [[nodiscard]] static arrow_proxy create_proxy(
397 R&& range,
398 bool = true,
399 std::optional<std::string_view> name = std::nullopt,
400 std::optional<METADATA_RANGE> metadata = std::nullopt
401 );
402
422 template <
423 std::ranges::input_range VALUE_BUFFERS_RANGE,
424 validity_bitmap_input VB,
425 input_metadata_container METADATA_RANGE = std::vector<metadata_pair>>
426 requires std::convertible_to<std::ranges::range_value_t<VALUE_BUFFERS_RANGE>, u8_buffer<uint8_t>>
427 [[nodiscard]] static arrow_proxy create_proxy(
428 size_t element_count,
429 u8_buffer<uint8_t>&& buffer_view,
430 VALUE_BUFFERS_RANGE&& value_buffers,
431 VB&& validity_input,
432 std::optional<std::string_view> name = std::nullopt,
433 std::optional<METADATA_RANGE> metadata = std::nullopt
434 );
435
447 [[nodiscard]] constexpr inner_reference value(size_type i);
448
463 [[nodiscard]] constexpr inner_const_reference value(size_type i) const;
464
484 template <std::ranges::sized_range U>
485 requires mpl::convertible_ranges<U, T>
486 constexpr void assign(U&& rhs, size_type index);
487
488 // Modifiers
489
506 template <std::ranges::sized_range U>
507 requires mpl::convertible_ranges<U, T>
508 void resize_values(size_type new_length, U value);
509
528 template <std::ranges::sized_range U>
529 requires mpl::convertible_ranges<U, T>
530 value_iterator insert_value(const_value_iterator pos, U value, size_type count);
531
551 template <mpl::iterator_of_type<T> InputIt>
552 value_iterator insert_values(const_value_iterator pos, InputIt first, InputIt last);
553
570 value_iterator erase_values(const_value_iterator pos, size_type count);
571
579 [[nodiscard]] constexpr value_iterator value_begin();
580
588 [[nodiscard]] constexpr value_iterator value_end();
589
597 [[nodiscard]] constexpr const_value_iterator value_cbegin() const;
598
606 [[nodiscard]] constexpr const_value_iterator value_cend() const;
607
608 static constexpr size_type LENGTH_BUFFER_INDEX = 1;
609 static constexpr std::size_t DATA_BUFFER_SIZE = 16;
610 static constexpr std::size_t SHORT_STRING_SIZE = 12;
611 static constexpr std::size_t PREFIX_SIZE = 4;
612 static constexpr std::ptrdiff_t PREFIX_OFFSET = 4;
613 static constexpr std::ptrdiff_t SHORT_STRING_OFFSET = 4;
614 static constexpr std::ptrdiff_t BUFFER_INDEX_OFFSET = 8;
615 static constexpr std::ptrdiff_t BUFFER_OFFSET_OFFSET = 12;
616 static constexpr std::size_t FIRST_VAR_DATA_BUFFER_INDEX = 2;
617
618 friend base_type;
623 };
624
625 template <std::ranges::sized_range T, class CR, typename Ext>
630
631 template <std::ranges::sized_range T, class CR, typename Ext>
637
638 namespace
639 {
640 // Utility functions for safe unaligned access to int32 values
641 inline std::int32_t read_int32_unaligned(const std::uint8_t* ptr)
642 {
643 std::int32_t value;
644 std::memcpy(&value, ptr, sizeof(std::int32_t));
645 return value;
646 }
647
648 inline void write_int32_unaligned(std::uint8_t* ptr, std::int32_t value)
649 {
650 std::memcpy(ptr, &value, sizeof(std::int32_t));
651 }
652
653 // Generic transformation function for type conversions
654 template <typename To, typename From>
655 inline constexpr To transform_to(const From& v)
656 {
657 return static_cast<To>(v);
658 }
659
660 // Helper function to update buffer offsets for long strings after a specific offset
661 template <typename SizeType>
662 inline void update_buffer_offsets_after(
663 std::uint8_t* view_data,
664 SizeType array_size,
665 std::size_t data_buffer_size,
666 std::size_t short_string_size,
667 std::ptrdiff_t buffer_offset_offset,
668 std::size_t threshold_offset,
669 std::ptrdiff_t offset_adjustment,
670 SizeType skip_index = static_cast<SizeType>(-1)
671 )
672 {
673 for (SizeType i = 0; i < array_size; ++i)
674 {
675 if (i == skip_index)
676 {
677 continue;
678 }
679
680 auto* view_ptr = view_data + (i * data_buffer_size);
681 std::int32_t length;
682 std::memcpy(&length, view_ptr, sizeof(std::int32_t));
683
684 if (static_cast<std::size_t>(length) > short_string_size)
685 {
686 std::int32_t current_offset;
687 std::memcpy(&current_offset, view_ptr + buffer_offset_offset, sizeof(std::int32_t));
688
689 if (static_cast<std::size_t>(current_offset) > threshold_offset)
690 {
691 current_offset += static_cast<std::int32_t>(offset_adjustment);
692 std::memcpy(view_ptr + buffer_offset_offset, &current_offset, sizeof(std::int32_t));
693 }
694 }
695 }
696 }
697
698 // Helper function to update buffer sizes metadata
699 template <typename Buffer>
700 inline void update_buffer_sizes_metadata(Buffer& buffer_sizes_buffer, std::int64_t new_size)
701 {
702 auto buffer_sizes_ptr = buffer_sizes_buffer.template data<std::int64_t>();
703 *buffer_sizes_ptr = new_size;
704 }
705 }
706
707 template <std::ranges::sized_range T, class CR, typename Ext>
708 template <std::ranges::input_range R>
709 requires std::convertible_to<std::ranges::range_value_t<R>, T>
710 auto variable_size_binary_view_array_impl<T, CR, Ext>::create_buffers(R&& range) -> buffers_collection
711 {
712#ifdef __GNUC__
713# pragma GCC diagnostic push
714# pragma GCC diagnostic ignored "-Wcast-align"
715#endif
716
717 const auto size = range_size(range);
718 buffer<uint8_t> length_buffer(size * DATA_BUFFER_SIZE, typename buffer<uint8_t>::default_allocator());
719
720 std::size_t long_string_storage_size = 0;
721 std::size_t i = 0;
722 for (auto&& val : range)
723 {
724 auto val_casted = val
725 | std::ranges::views::transform(transform_to<std::uint8_t, typename T::value_type>);
726
727 const auto length = val.size();
728 auto length_ptr = length_buffer.data() + (i * DATA_BUFFER_SIZE);
729
730 // write length
731 write_int32_unaligned(length_ptr, static_cast<std::int32_t>(length));
732
733 if (length <= SHORT_STRING_SIZE)
734 {
735 // write data itself
736 sparrow::ranges::copy(val_casted, length_ptr + SHORT_STRING_OFFSET);
737 std::fill(
738 length_ptr + SHORT_STRING_OFFSET + length,
739 length_ptr + DATA_BUFFER_SIZE,
740 std::uint8_t(0)
741 );
742 }
743 else
744 {
745 // write the prefix of the data
746 auto prefix_sub_range = val_casted | std::ranges::views::take(PREFIX_SIZE);
747 sparrow::ranges::copy(prefix_sub_range, length_ptr + PREFIX_OFFSET);
748
749 // write the buffer index
750 write_int32_unaligned(length_ptr + BUFFER_INDEX_OFFSET, 0);
751
752 // write the buffer offset
753 write_int32_unaligned(
754 length_ptr + BUFFER_OFFSET_OFFSET,
755 static_cast<std::int32_t>(long_string_storage_size)
756 );
757
758 // count the size of the long string storage
759 long_string_storage_size += length;
760 }
761 ++i;
762 }
763
764 // write the long string storage
765 buffer<uint8_t> long_string_storage(long_string_storage_size, buffer<uint8_t>::default_allocator());
766 std::size_t long_string_storage_offset = 0;
767 for (auto&& val : range)
768 {
769 const auto length = val.size();
770 if (length > SHORT_STRING_SIZE)
771 {
772 auto val_casted = val
773 | std::ranges::views::transform(
774 transform_to<std::uint8_t, typename T::value_type>
775 );
776 sparrow::ranges::copy(val_casted, long_string_storage.data() + long_string_storage_offset);
777 long_string_storage_offset += length;
778 }
779 }
780
781 // For binary or utf-8 view arrays, an extra buffer is appended which stores
782 // the lengths of each variadic data buffer as int64_t.
783 // This buffer is necessary since these buffer lengths are not trivially
784 // extractable from other data in an array of binary or utf-8 view type.
785 u8_buffer<int64_t> buffer_sizes(
786 static_cast<std::size_t>(1),
787 static_cast<int64_t>(long_string_storage_size)
788 );
789
790 return {std::move(length_buffer), std::move(long_string_storage), std::move(buffer_sizes)};
791
792#ifdef __GNUC__
793# pragma GCC diagnostic pop
794#endif
795 }
796
797 template <std::ranges::sized_range T, class CR, typename Ext>
798 template <std::ranges::input_range R, validity_bitmap_input VB, input_metadata_container METADATA_RANGE>
799 requires std::convertible_to<std::ranges::range_value_t<R>, T>
800 arrow_proxy variable_size_binary_view_array_impl<T, CR, Ext>::create_proxy(
801 R&& range,
802 VB&& validity_input,
803 std::optional<std::string_view> name,
804 std::optional<METADATA_RANGE> metadata
805 )
806 {
807 const auto size = range_size(range);
808 validity_bitmap vbitmap = ensure_validity_bitmap(size, std::forward<VB>(validity_input));
809 const auto null_count = vbitmap.null_count();
810
811 static const std::optional<std::unordered_set<sparrow::ArrowFlag>> flags{{ArrowFlag::NULLABLE}};
812
813 // create arrow schema
814 ArrowSchema schema = create_arrow_schema(std::move(name), std::move(metadata), flags);
815
816 // create buffers
817 auto buffers_parts = create_buffers(std::forward<R>(range));
818
819 std::vector<buffer<uint8_t>> buffers;
820 buffers.reserve(4);
821 buffers.emplace_back(std::move(vbitmap).extract_storage());
822 buffers.emplace_back(std::move(buffers_parts.length_buffer));
823 buffers.emplace_back(std::move(buffers_parts.long_string_storage));
824 buffers.emplace_back(std::move(buffers_parts.buffer_sizes).extract_storage());
825
826 constexpr repeat_view<bool> children_ownership(true, 0);
827
828 // create arrow array
829 ArrowArray arr = make_arrow_array(
830 static_cast<std::int64_t>(size), // length
831 static_cast<int64_t>(null_count),
832 0, // offset
833 std::move(buffers),
834 nullptr, // children
836 nullptr, // dictionary
837 true
838 );
839
840 arrow_proxy proxy{std::move(arr), std::move(schema)};
841 Ext::init(proxy);
842 return proxy;
843 }
844
845 template <std::ranges::sized_range T, class CR, typename Ext>
846 template <std::ranges::input_range NULLABLE_RANGE, input_metadata_container METADATA_RANGE>
847 requires std::convertible_to<std::ranges::range_value_t<NULLABLE_RANGE>, nullable<T>>
848 [[nodiscard]] arrow_proxy variable_size_binary_view_array_impl<T, CR, Ext>::create_proxy(
849 NULLABLE_RANGE&& nullable_range,
850 std::optional<std::string_view> name,
851 std::optional<METADATA_RANGE> metadata
852 )
853 {
854 auto values = nullable_range
855 | std::views::transform(
856 [](const auto& v)
857 {
858 return static_cast<T>(v.value());
859 }
860 );
861
862 auto is_non_null = nullable_range
863 | std::views::transform(
864 [](const auto& v)
865 {
866 return v.has_value();
867 }
868 );
869
870 return create_proxy(
871 std::forward<decltype(values)>(values),
872 std::forward<decltype(is_non_null)>(is_non_null),
873 name,
874 metadata
875 );
876 }
877
878 template <std::ranges::sized_range T, class CR, typename Ext>
879 template <std::ranges::input_range R, input_metadata_container METADATA_RANGE>
880 requires std::convertible_to<std::ranges::range_value_t<R>, T>
881 [[nodiscard]] arrow_proxy variable_size_binary_view_array_impl<T, CR, Ext>::create_proxy(
882 R&& range,
883 bool nullable,
884 std::optional<std::string_view> name,
885 std::optional<METADATA_RANGE> metadata
886 )
887 {
888 if (nullable)
889 {
890 return create_proxy(
891 std::forward<R>(range),
893 std::move(name),
894 std::move(metadata)
895 );
896 }
897
898 // create arrow schema
899 ArrowSchema schema = create_arrow_schema(std::move(name), std::move(metadata), std::nullopt);
900
901 // create buffers
902 auto buffers_parts = create_buffers(std::forward<R>(range));
903
904 std::vector<buffer<uint8_t>> buffers;
905 buffers.reserve(4);
906 buffers.emplace_back(nullptr, 0, buffer<uint8_t>::default_allocator()); // validity bitmap
907 buffers.emplace_back(std::move(buffers_parts.length_buffer));
908 buffers.emplace_back(std::move(buffers_parts.long_string_storage));
909 buffers.emplace_back(std::move(buffers_parts.buffer_sizes).extract_storage());
910 const auto size = range_size(range);
911
912 constexpr repeat_view<bool> children_ownership(true, 0);
913
914 // create arrow array
915 ArrowArray arr = make_arrow_array(
916 static_cast<std::int64_t>(size), // length
917 static_cast<int64_t>(0),
918 0, // offset
919 std::move(buffers),
920 nullptr, // children
922 nullptr, // dictionary
923 true
924 );
925
926 arrow_proxy proxy{std::move(arr), std::move(schema)};
927 Ext::init(proxy);
928 return proxy;
929 }
930
931 template <std::ranges::sized_range T, class CR, typename Ext>
932 template <std::ranges::input_range VALUE_BUFFERS_RANGE, validity_bitmap_input VB, input_metadata_container METADATA_RANGE>
933 requires std::convertible_to<std::ranges::range_value_t<VALUE_BUFFERS_RANGE>, u8_buffer<uint8_t>>
934 arrow_proxy variable_size_binary_view_array_impl<T, CR, Ext>::create_proxy(
935 size_t element_count,
937 VALUE_BUFFERS_RANGE&& value_buffers,
938 VB&& validity_input,
939 std::optional<std::string_view> name,
940 std::optional<METADATA_RANGE> metadata
941 )
942 {
943 const auto size = buffer_view.size() / DATA_BUFFER_SIZE;
944 SPARROW_ASSERT_TRUE(size == element_count);
945
946 static const std::optional<std::unordered_set<sparrow::ArrowFlag>> flags{{ArrowFlag::NULLABLE}};
947
948 ArrowSchema schema = create_arrow_schema(std::move(name), std::move(metadata), flags);
949
950 auto bitmap = ensure_validity_bitmap(size, std::forward<VB>(validity_input));
951 std::vector<buffer<uint8_t>> buffers;
952 buffers.reserve(2 + std::ranges::size(value_buffers));
953 buffers.emplace_back(std::move(bitmap).extract_storage());
954 buffers.emplace_back(std::move(buffer_view).extract_storage());
955
956 // Extract sizes before moving buffers
957 {
958 u8_buffer<int64_t> buffer_sizes(std::ranges::size(value_buffers));
959 size_t i = 0;
960 for (auto&& buf : value_buffers)
961 {
962 buffer_sizes[i] = static_cast<int64_t>(buf.size());
963 buffers.emplace_back(std::move(buf).extract_storage());
964 ++i;
965 }
966 buffers.push_back(std::move(buffer_sizes).extract_storage());
967 }
968
969 constexpr repeat_view<bool> children_ownership(true, 0);
970
971 ArrowArray arr = make_arrow_array(
972 static_cast<std::int64_t>(size), // length
973 static_cast<std::int64_t>(bitmap.null_count()), // null_count
974 0, // offset
975 std::move(buffers),
976 nullptr, // children
978 nullptr, // dictionary
979 true
980 );
981
982 arrow_proxy proxy{std::move(arr), std::move(schema)};
983 Ext::init(proxy);
984 return proxy;
985 }
986
987 template <std::ranges::sized_range T, class CR, typename Ext>
988 constexpr auto variable_size_binary_view_array_impl<T, CR, Ext>::value(size_type i) -> inner_reference
989 {
990 return static_cast<const self_type*>(this)->value(i);
991 }
992
993 template <std::ranges::sized_range T, class CR, typename Ext>
994 constexpr auto variable_size_binary_view_array_impl<T, CR, Ext>::value(size_type i) const
995 -> inner_const_reference
996 {
997#ifdef __GNUC__
998# pragma GCC diagnostic push
999# pragma GCC diagnostic ignored "-Wcast-align"
1000#endif
1001
1002 SPARROW_ASSERT_TRUE(i < this->size());
1003 using char_or_byte = typename inner_const_reference::value_type;
1004
1005 auto data_ptr = this->get_arrow_proxy().buffers()[LENGTH_BUFFER_INDEX].template data<uint8_t>()
1006 + (i * DATA_BUFFER_SIZE);
1007 const auto length = static_cast<std::size_t>(read_int32_unaligned(data_ptr));
1008
1009 if (length <= SHORT_STRING_SIZE)
1010 {
1011 const auto ptr = reinterpret_cast<const char_or_byte*>(data_ptr);
1012 const auto ret = inner_const_reference(ptr + SHORT_STRING_OFFSET, length);
1013 return ret;
1014 }
1015 else
1016 {
1017 const auto buffer_index = static_cast<std::size_t>(
1018 read_int32_unaligned(data_ptr + BUFFER_INDEX_OFFSET)
1019 );
1020 const auto buffer_offset = static_cast<std::size_t>(
1021 read_int32_unaligned(data_ptr + BUFFER_OFFSET_OFFSET)
1022 );
1023 const auto buffer = this->get_arrow_proxy()
1024 .buffers()[buffer_index + FIRST_VAR_DATA_BUFFER_INDEX]
1025 .template data<const char_or_byte>();
1026 return inner_const_reference(buffer + buffer_offset, length);
1027 }
1028
1029#ifdef __GNUC__
1030# pragma GCC diagnostic pop
1031#endif
1032 }
1033
1034 template <std::ranges::sized_range T, class CR, typename Ext>
1035 constexpr auto variable_size_binary_view_array_impl<T, CR, Ext>::value_begin() -> value_iterator
1036 {
1038 }
1039
1040 template <std::ranges::sized_range T, class CR, typename Ext>
1041 constexpr auto variable_size_binary_view_array_impl<T, CR, Ext>::value_end() -> value_iterator
1042 {
1043 return value_iterator(detail::layout_value_functor<self_type, inner_reference>(this), this->size());
1044 }
1045
1046 template <std::ranges::sized_range T, class CR, typename Ext>
1047 constexpr auto variable_size_binary_view_array_impl<T, CR, Ext>::value_cbegin() const
1048 -> const_value_iterator
1049 {
1051 }
1052
1053 template <std::ranges::sized_range T, class CR, typename Ext>
1054 constexpr auto variable_size_binary_view_array_impl<T, CR, Ext>::value_cend() const -> const_value_iterator
1055 {
1056 return const_value_iterator(
1058 this->size()
1059 );
1060 }
1061
1062 template <std::ranges::sized_range T, class CR, typename Ext>
1063 template <std::ranges::sized_range U>
1065 constexpr void variable_size_binary_view_array_impl<T, CR, Ext>::assign(U&& rhs, size_type index)
1066 {
1067 SPARROW_ASSERT_TRUE(index < this->size());
1068 const auto new_length = static_cast<std::size_t>(std::ranges::size(rhs));
1069
1070 auto& length_buffer = this->get_arrow_proxy().get_array_private_data()->buffers()[LENGTH_BUFFER_INDEX];
1071 auto view_ptr = length_buffer.data() + (index * DATA_BUFFER_SIZE);
1072 const auto current_length = static_cast<std::size_t>(read_int32_unaligned(view_ptr));
1073
1074 // Update the length in the view structure
1075 write_int32_unaligned(view_ptr, static_cast<std::int32_t>(new_length));
1076
1077 if (new_length <= SHORT_STRING_SIZE)
1078 {
1079 auto data_ptr = view_ptr + SHORT_STRING_OFFSET;
1080 std::ranges::copy(rhs, reinterpret_cast<typename T::value_type*>(data_ptr));
1081
1082 // Clear any remaining bytes in the inline storage
1083 if (new_length < SHORT_STRING_SIZE)
1084 {
1085 std::fill_n(
1086 reinterpret_cast<typename T::value_type*>(data_ptr) + new_length,
1087 SHORT_STRING_SIZE - new_length,
1088 typename T::value_type{}
1089 );
1090 }
1091 }
1092 else
1093 {
1094 // Handle assignment of long strings (> 12 bytes)
1095 // This requires managing the variadic buffers and potentially reorganizing the layout
1096
1097 auto& buffers = this->get_arrow_proxy().get_array_private_data()->buffers();
1098 auto& var_data_buffer = buffers[FIRST_VAR_DATA_BUFFER_INDEX];
1099 auto& buffer_sizes_buffer = buffers[buffers.size() - 1]; // Last buffer contains sizes
1100
1101 const bool was_long_string = current_length > SHORT_STRING_SIZE;
1102 std::size_t current_buffer_offset = 0;
1103
1104 if (was_long_string)
1105 {
1106 current_buffer_offset = static_cast<std::size_t>(
1107 read_int32_unaligned(view_ptr + BUFFER_OFFSET_OFFSET)
1108 );
1109 }
1110
1111 auto transformed_data = rhs
1112 | std::ranges::views::transform(
1113 transform_to<typename T::value_type, typename T::value_type>
1114 );
1115
1116 // Check for memory reuse optimization: if the new value is identical to existing data
1117 bool can_reuse_memory = false;
1118 if (was_long_string && new_length == current_length)
1119 {
1120 const auto* existing_data = var_data_buffer.data() + current_buffer_offset;
1121 can_reuse_memory = std::ranges::equal(
1122 transformed_data,
1123 std::span<const typename T::value_type>(
1124 reinterpret_cast<const typename T::value_type*>(existing_data),
1125 new_length
1126 )
1127 );
1128 }
1129
1130 if (can_reuse_memory)
1131 {
1132 // Data is identical - just update the view structure prefix and we're done
1133 auto prefix_range = rhs | std::ranges::views::take(PREFIX_SIZE);
1134 auto prefix_transformed = prefix_range
1135 | std::ranges::views::transform(
1136 transform_to<std::uint8_t, typename T::value_type>
1137 );
1138 std::ranges::copy(prefix_transformed, view_ptr + PREFIX_OFFSET);
1139 return; // Early exit - no buffer management needed
1140 }
1141
1142 // Calculate space requirements and buffer management strategy
1143 const auto length_diff = static_cast<std::ptrdiff_t>(new_length)
1144 - static_cast<std::ptrdiff_t>(current_length);
1145 const bool can_fit_in_place = was_long_string && length_diff <= 0;
1146
1147 std::size_t final_offset = 0;
1148
1149 if (can_fit_in_place)
1150 {
1151 // We can reuse the existing space (new data is same size or smaller)
1152 final_offset = current_buffer_offset;
1153
1154 // If the new data is smaller, we need to compact the buffer
1155 if (length_diff < 0)
1156 {
1157 const auto bytes_to_compact = static_cast<std::size_t>(-length_diff);
1158 const auto move_start = current_buffer_offset + current_length;
1159 const auto move_end = var_data_buffer.size();
1160 const auto bytes_to_move = move_end - move_start;
1161
1162 if (bytes_to_move > 0)
1163 {
1164 std::move(
1165 var_data_buffer.data() + move_start,
1166 var_data_buffer.data() + move_end,
1167 var_data_buffer.data() + move_start - bytes_to_compact
1168 );
1169 }
1170
1171 var_data_buffer.resize(var_data_buffer.size() - bytes_to_compact);
1172
1173 // Update buffer offsets for all elements that come after this one
1174 update_buffer_offsets_after(
1175 length_buffer.data(),
1176 this->size(),
1177 DATA_BUFFER_SIZE,
1178 SHORT_STRING_SIZE,
1179 BUFFER_OFFSET_OFFSET,
1180 current_buffer_offset + current_length,
1181 -static_cast<std::ptrdiff_t>(bytes_to_compact),
1182 index
1183 );
1184
1185 // Update buffer sizes metadata
1186 update_buffer_sizes_metadata(
1187 buffer_sizes_buffer,
1188 static_cast<std::int64_t>(var_data_buffer.size())
1189 );
1190 }
1191 }
1192 else
1193 {
1194 // Need to expand buffer or assign to new location
1195 const auto expansion_needed = was_long_string ? length_diff
1196 : static_cast<std::ptrdiff_t>(new_length);
1197 const auto new_var_buffer_size = var_data_buffer.size() + expansion_needed;
1198
1199 if (was_long_string && length_diff > 0)
1200 {
1201 // Expand in-place: move data after current element to make space
1202 final_offset = current_buffer_offset;
1203 const auto expansion_bytes = static_cast<std::size_t>(length_diff);
1204 const auto move_start = current_buffer_offset + current_length;
1205 const auto bytes_to_move = var_data_buffer.size() - move_start;
1206
1207 // Resize buffer first
1208 var_data_buffer.resize(new_var_buffer_size);
1209
1210 if (bytes_to_move > 0)
1211 {
1212 // Move data to make space for expansion
1213 std::move_backward(
1214 var_data_buffer.data() + move_start,
1215 var_data_buffer.data() + move_start + bytes_to_move,
1216 var_data_buffer.data() + move_start + bytes_to_move + expansion_bytes
1217 );
1218 }
1219
1220 // Update buffer offsets for all elements that come after this one
1221 update_buffer_offsets_after(
1222 length_buffer.data(),
1223 this->size(),
1224 DATA_BUFFER_SIZE,
1225 SHORT_STRING_SIZE,
1226 BUFFER_OFFSET_OFFSET,
1227 move_start - 1, // threshold is just before move_start
1228 static_cast<std::ptrdiff_t>(expansion_bytes),
1229 index
1230 );
1231 }
1232 else
1233 {
1234 // Append to end of buffer (new long string)
1235 final_offset = var_data_buffer.size();
1236 var_data_buffer.resize(new_var_buffer_size);
1237 }
1238
1239 // Update buffer sizes metadata
1240 update_buffer_sizes_metadata(buffer_sizes_buffer, static_cast<std::int64_t>(new_var_buffer_size));
1241 }
1242
1243 std::ranges::copy(transformed_data, var_data_buffer.data() + final_offset);
1244
1245 // Update view structure for long string format
1246 // Write prefix (first 4 bytes)
1247 auto prefix_range = rhs | std::ranges::views::take(PREFIX_SIZE);
1248 auto prefix_transformed = prefix_range
1249 | std::ranges::views::transform(
1250 transform_to<std::uint8_t, typename T::value_type>
1251 );
1252 std::ranges::copy(prefix_transformed, view_ptr + PREFIX_OFFSET);
1253
1254 write_int32_unaligned(
1255 view_ptr + BUFFER_INDEX_OFFSET,
1256 static_cast<std::int32_t>(FIRST_VAR_DATA_BUFFER_INDEX)
1257 );
1258
1259 write_int32_unaligned(view_ptr + BUFFER_OFFSET_OFFSET, static_cast<std::int32_t>(final_offset));
1260 }
1261 }
1262
1263 template <std::ranges::sized_range T, class CR, typename Ext>
1264 template <std::ranges::sized_range U>
1266 void variable_size_binary_view_array_impl<T, CR, Ext>::resize_values(size_type new_length, U value)
1267 {
1268 const size_t current_size = this->size();
1269
1270 if (new_length == current_size)
1271 {
1272 return;
1273 }
1274
1275 if (new_length < current_size)
1276 {
1277 erase_values(sparrow::next(value_cbegin(), new_length), current_size - new_length);
1278 }
1279 else
1280 {
1281 insert_value(value_cend(), value, new_length - current_size);
1282 }
1283 }
1284
1285 template <std::ranges::sized_range T, class CR, typename Ext>
1286 template <std::ranges::sized_range U>
1288 auto
1289 variable_size_binary_view_array_impl<T, CR, Ext>::insert_value(const_value_iterator pos, U value, size_type count)
1291 {
1292 const auto repeat_view = sparrow::repeat_view<U>(value, count);
1293 return insert_values(pos, std::ranges::begin(repeat_view), std::ranges::end(repeat_view));
1294 }
1295
1296 template <std::ranges::sized_range T, class CR, typename Ext>
1297 template <mpl::iterator_of_type<T> InputIt>
1298 auto variable_size_binary_view_array_impl<T, CR, Ext>::insert_values(
1300 InputIt first,
1301 InputIt last
1302 ) -> value_iterator
1303 {
1304 SPARROW_ASSERT_TRUE(first <= last);
1305 const size_type count = static_cast<size_type>(std::distance(first, last));
1306 if (count == 0)
1307 {
1308 const auto insert_index = std::distance(value_cbegin(), pos);
1309 return value_begin() + insert_index;
1310 }
1311
1312 const auto insert_index = static_cast<size_t>(std::distance(value_cbegin(), pos));
1313 const auto current_size = this->size();
1314 const auto new_size = current_size + count;
1315
1316 // Calculate total additional variadic storage needed
1317 std::size_t additional_var_storage = 0;
1318 std::vector<std::size_t> value_lengths;
1319 value_lengths.reserve(count);
1320
1321 for (auto it = first; it != last; ++it)
1322 {
1323 const auto length = static_cast<std::size_t>(std::ranges::size(*it));
1324 value_lengths.push_back(length);
1325 if (length > SHORT_STRING_SIZE)
1326 {
1327 additional_var_storage += length;
1328 }
1329 }
1330
1331 auto& proxy = this->get_arrow_proxy();
1332 auto* private_data = proxy.get_array_private_data();
1333 auto& buffers = private_data->buffers();
1334
1335 const auto new_view_buffer_size = new_size * DATA_BUFFER_SIZE;
1336 buffers[LENGTH_BUFFER_INDEX].resize(new_view_buffer_size);
1337
1338 if (additional_var_storage > 0)
1339 {
1340 const auto current_var_size = buffers[FIRST_VAR_DATA_BUFFER_INDEX].size();
1341 buffers[FIRST_VAR_DATA_BUFFER_INDEX].resize(current_var_size + additional_var_storage);
1342 }
1343
1344 auto& buffer_sizes = buffers[buffers.size() - 1];
1345 update_buffer_sizes_metadata(
1346 buffer_sizes,
1347 static_cast<std::int64_t>(buffers[FIRST_VAR_DATA_BUFFER_INDEX].size())
1348 );
1349
1350 // Shift existing view structures after insertion point
1351 auto* view_data = buffers[LENGTH_BUFFER_INDEX].data();
1352 if (insert_index < current_size)
1353 {
1354 const auto bytes_to_move = (current_size - insert_index) * DATA_BUFFER_SIZE;
1355 const auto src_offset = insert_index * DATA_BUFFER_SIZE;
1356 const auto dst_offset = (insert_index + count) * DATA_BUFFER_SIZE;
1357
1358 std::memmove(view_data + dst_offset, view_data + src_offset, bytes_to_move);
1359
1360 // Update buffer offsets for moved long strings
1361 if (additional_var_storage > 0)
1362 {
1363 for (size_type i = insert_index + count; i < new_size; ++i)
1364 {
1365 auto* view_ptr = view_data + (i * DATA_BUFFER_SIZE);
1366 std::int32_t length;
1367 std::memcpy(&length, view_ptr, sizeof(std::int32_t));
1368
1369 if (static_cast<std::size_t>(length) > SHORT_STRING_SIZE)
1370 {
1371 std::int32_t current_offset;
1372 std::memcpy(&current_offset, view_ptr + BUFFER_OFFSET_OFFSET, sizeof(std::int32_t));
1373 current_offset += static_cast<std::int32_t>(additional_var_storage);
1374 std::memcpy(view_ptr + BUFFER_OFFSET_OFFSET, &current_offset, sizeof(std::int32_t));
1375 }
1376 }
1377 }
1378 }
1379
1380 // Insert new view structures
1381 std::size_t var_offset = buffers[FIRST_VAR_DATA_BUFFER_INDEX].size() - additional_var_storage;
1382 size_type value_idx = 0;
1383
1384 for (auto it = first; it != last; ++it, ++value_idx)
1385 {
1386 const auto view_index = insert_index + value_idx;
1387 auto* view_ptr = view_data + (view_index * DATA_BUFFER_SIZE);
1388 const auto value_length = value_lengths[value_idx];
1389
1390 const auto& current_value = *it;
1391
1392 // Write length
1393 const std::int32_t value_length_int32 = static_cast<std::int32_t>(value_length);
1394 std::memcpy(view_ptr, &value_length_int32, sizeof(std::int32_t));
1395
1396 if (value_length <= SHORT_STRING_SIZE)
1397 {
1398 // Store inline - convert and copy elements manually
1399 std::ranges::transform(
1400 current_value,
1401 view_ptr + SHORT_STRING_OFFSET,
1402 transform_to<std::uint8_t, typename T::value_type>
1403 );
1404
1405 std::fill(
1406 view_ptr + SHORT_STRING_OFFSET + value_length,
1407 view_ptr + DATA_BUFFER_SIZE,
1408 std::uint8_t(0)
1409 );
1410 }
1411 else
1412 {
1413 // Store prefix - copy first PREFIX_SIZE elements manually
1414 std::ranges::transform(
1415 current_value | std::views::take(PREFIX_SIZE),
1416 view_ptr + PREFIX_OFFSET,
1417 transform_to<std::uint8_t, typename T::value_type>
1418 );
1419
1420 // Set buffer index
1421 const std::int32_t buffer_index_zero = 0;
1422 std::memcpy(view_ptr + BUFFER_INDEX_OFFSET, &buffer_index_zero, sizeof(std::int32_t));
1423
1424 // Set buffer offset
1425 const std::int32_t var_offset_int32 = static_cast<std::int32_t>(var_offset);
1426 std::memcpy(view_ptr + BUFFER_OFFSET_OFFSET, &var_offset_int32, sizeof(std::int32_t));
1427
1428 // Copy data to variadic buffer - convert and copy manually
1429 std::ranges::transform(
1430 current_value,
1431 buffers[FIRST_VAR_DATA_BUFFER_INDEX].data() + var_offset,
1432 transform_to<std::uint8_t, typename T::value_type>
1433 );
1434
1435 var_offset += value_length;
1436 }
1437 }
1438
1439 // Update buffers
1440 proxy.update_buffers();
1441
1442 return value_begin() + static_cast<difference_type>(insert_index);
1443 }
1444
1445 template <std::ranges::sized_range T, class CR, typename Ext>
1446 auto
1447 variable_size_binary_view_array_impl<T, CR, Ext>::erase_values(const_value_iterator pos, size_type count)
1448 -> value_iterator
1449 {
1450 const size_t erase_index = static_cast<size_t>(std::distance(value_cbegin(), pos));
1451 const size_t current_size = this->size();
1452
1453 // Validate bounds and handle zero count
1454 if (erase_index + count > current_size)
1455 {
1456 count = current_size - erase_index;
1457 }
1458
1459 if (count == 0)
1460 {
1461 return value_begin() + static_cast<difference_type>(erase_index);
1462 }
1463
1464 const auto new_size = current_size - count;
1465
1466 // Calculate how much variadic storage will be freed
1467 std::size_t freed_var_storage = 0;
1468 auto& proxy = this->get_arrow_proxy();
1469 auto* private_data = proxy.get_array_private_data();
1470 auto& buffers = private_data->buffers();
1471 auto* view_data = buffers[LENGTH_BUFFER_INDEX].data();
1472
1473 // Calculate freed storage from elements being erased
1474 for (size_type i = erase_index; i < erase_index + count; ++i)
1475 {
1476 auto* view_ptr = view_data + (i * DATA_BUFFER_SIZE);
1477 std::int32_t length;
1478 std::memcpy(&length, view_ptr, sizeof(std::int32_t));
1479 if (static_cast<std::size_t>(length) > SHORT_STRING_SIZE)
1480 {
1481 freed_var_storage += static_cast<std::size_t>(length);
1482 }
1483 }
1484
1485 // Handle empty array case
1486 if (new_size == 0)
1487 {
1488 // Resize all buffers to empty
1489 if (buffers[0].size() > 0)
1490 {
1491 buffers[0].clear();
1492 }
1493 buffers[LENGTH_BUFFER_INDEX].clear();
1494 buffers[FIRST_VAR_DATA_BUFFER_INDEX].clear();
1495
1496 auto& buffer_sizes = buffers[buffers.size() - 1];
1497 update_buffer_sizes_metadata(buffer_sizes, 0);
1498
1499 proxy.update_buffers();
1500 return value_begin();
1501 }
1502
1503 // Compact variadic buffer if needed
1504 if (freed_var_storage > 0)
1505 {
1506 auto& var_buffer = buffers[FIRST_VAR_DATA_BUFFER_INDEX];
1507 std::size_t write_offset = 0;
1508
1509 // Create mapping of old offsets to new offsets
1510 std::unordered_map<std::size_t, std::size_t> offset_mapping;
1511 offset_mapping.reserve(current_size - count);
1512
1513 for (size_type i = 0; i < current_size; ++i)
1514 {
1515 if (i >= erase_index && i < erase_index + count)
1516 {
1517 // Skip erased elements
1518 continue;
1519 }
1520
1521 auto* view_ptr = view_data + (i * DATA_BUFFER_SIZE);
1522 std::int32_t length;
1523 std::memcpy(&length, view_ptr, sizeof(std::int32_t));
1524 if (static_cast<std::size_t>(length) > SHORT_STRING_SIZE)
1525 {
1526 std::int32_t old_offset_int32;
1527 std::memcpy(&old_offset_int32, view_ptr + BUFFER_OFFSET_OFFSET, sizeof(std::int32_t));
1528 const auto old_offset = static_cast<std::size_t>(old_offset_int32);
1529
1530 // Record mapping for updating view structures later
1531 offset_mapping[old_offset] = write_offset;
1532
1533 // Move data if needed
1534 if (write_offset != old_offset)
1535 {
1536 std::memmove(
1537 var_buffer.data() + write_offset,
1538 var_buffer.data() + old_offset,
1539 static_cast<std::size_t>(length)
1540 );
1541 }
1542
1543 write_offset += static_cast<std::size_t>(length);
1544 }
1545 }
1546
1547 // Resize variadic buffer
1548 var_buffer.resize(var_buffer.size() - freed_var_storage);
1549
1550 // Update buffer sizes metadata
1551 auto& buffer_sizes = buffers[buffers.size() - 1];
1552 update_buffer_sizes_metadata(buffer_sizes, static_cast<std::int64_t>(var_buffer.size()));
1553
1554 // Update view structure offsets
1555 for (size_type i = 0; i < current_size; ++i)
1556 {
1557 if (i >= erase_index && i < erase_index + count)
1558 {
1559 continue; // Skip erased elements
1560 }
1561
1562 auto* view_ptr = view_data + (i * DATA_BUFFER_SIZE);
1563 std::int32_t length;
1564 std::memcpy(&length, view_ptr, sizeof(std::int32_t));
1565 if (static_cast<std::size_t>(length) > SHORT_STRING_SIZE)
1566 {
1567 std::int32_t old_offset_int32;
1568 std::memcpy(&old_offset_int32, view_ptr + BUFFER_OFFSET_OFFSET, sizeof(std::int32_t));
1569 const auto old_offset = static_cast<std::size_t>(old_offset_int32);
1570 auto it = offset_mapping.find(old_offset);
1571 if (it != offset_mapping.end())
1572 {
1573 const std::int32_t new_offset = static_cast<std::int32_t>(it->second);
1574 std::memcpy(view_ptr + BUFFER_OFFSET_OFFSET, &new_offset, sizeof(std::int32_t));
1575 }
1576 }
1577 }
1578 }
1579
1580 // Compact view buffer - move elements after erase range
1581 if (erase_index + count < current_size)
1582 {
1583 const auto src_offset = (erase_index + count) * DATA_BUFFER_SIZE;
1584 const auto dst_offset = erase_index * DATA_BUFFER_SIZE;
1585 const auto bytes_to_move = (current_size - erase_index - count) * DATA_BUFFER_SIZE;
1586
1587 std::memmove(view_data + dst_offset, view_data + src_offset, bytes_to_move);
1588 }
1589
1590 // Resize view buffer
1591 buffers[LENGTH_BUFFER_INDEX].resize(new_size * DATA_BUFFER_SIZE);
1592
1593 // Update buffers
1594 proxy.update_buffers();
1595
1596 // Return iterator to element after last erased, or end if we erased to the end
1597 return erase_index < new_size ? sparrow::next(value_begin(), erase_index) : value_end();
1598 }
1599
1600}
typename base_type::const_bitmap_range const_bitmap_range
typename base_type::bitmap_iterator bitmap_iterator
typename base_type::iterator_tag iterator_tag
typename base_type::const_bitmap_iterator const_bitmap_iterator
std::conditional_t< is_mutable, mutable_array_base< D >, array_crtp_base< D > > base_type
typename base_type::bitmap_const_reference bitmap_const_reference
typename base_type::bitmap_type bitmap_type
typename base_type::difference_type difference_type
constexpr size_type size() const noexcept
Object that owns a piece of contiguous memory.
Definition buffer.hpp:131
xsimd::aligned_allocator< T > default_allocator
Definition buffer.hpp:144
constexpr U * data() noexcept
Definition buffer.hpp:653
constexpr size_type null_count() const noexcept
Returns the number of bits set to false (null/invalid).
typename storage_type::default_allocator default_allocator
A view that repeats a value a given number of times.
This buffer class is used as storage buffer for all sparrow arrays.
variable_size_binary_view_array_impl(Args &&... args)
Generic constructor for creating variable-size binary view array.
self_type & operator=(const self_type &)=default
variable_size_binary_view_array_impl(arrow_proxy)
Constructs variable-size binary view array from Arrow proxy.
Concept for convertible range types.
Definition mp_utils.hpp:931
#define SPARROW_ASSERT_TRUE(expr__)
SPARROW_API void increase(const std::string &key)
SPARROW_API int count(const std::string &key, int disabled_value=0)
std::string key()
Definition buffer.hpp:49
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.
constexpr bool is_type_instance_of_v
Variable template for convenient access to is_type_instance_of.
Definition mp_utils.hpp:102
constexpr std::ranges::copy_result< std::ranges::borrowed_iterator_t< R >, O > copy(R &&r, O result)
Definition ranges.hpp:120
array_bitmap_base_impl< D, true > mutable_array_bitmap_base
Convenient alias for arrays with mutable validity bitmaps.
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 bool is_variable_size_binary_view_array
Checks whether T is a variable_size_binary_view_array_impl type.
SPARROW_API std::size_t array_size(const array_wrapper &ar)
constexpr InputIt next(InputIt it, Distance n)
Definition iterator.hpp:503
constexpr std::size_t range_size(R &&r)
Definition ranges.hpp:35
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.
variable_size_binary_view_array_impl< arrow_traits< std::string >::value_type, arrow_traits< std::string >::const_reference > string_view_array
A variable-size string view layout implementation.
dynamic_bitset< std::uint8_t > validity_bitmap
Type alias for a validity bitmap using 8-bit storage blocks.
validity_bitmap ensure_validity_bitmap(std::size_t size, R &&validity_input)
Ensures a validity bitmap of the specified size from various input types.
variable_size_binary_view_array_impl< arrow_traits< std::vector< byte_t > >::value_type, arrow_traits< std::vector< byte_t > >::const_reference > binary_view_array
A variable-size binary view layout implementation.
data_type
Runtime identifier of arrow data types, usually associated with raw bytes with the associated value.
Extensions to the C++ standard library.
functor_index_iterator< detail::layout_value_functor< const array_type, inner_const_reference > > const_value_iterator
functor_index_iterator< detail::layout_value_functor< array_type, inner_reference > > value_iterator
Base class for array_inner_types specializations.
Traits class that must be specialized by array implementations.
Provides compile-time information about Arrow data types.
Metafunction for retrieving the data_type of a typed array.