MCPcopy Create free account
hub / github.com/apache/arrow / unpack_exact

Function unpack_exact

cpp/src/arrow/util/bpacking_dispatch_internal.h:100–178  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

98/// stop if it finds a byte aligned value start.
99template <int kPackedBitWidth, bool kIsProlog, typename Uint>
100ARROW_FORCE_INLINE int unpack_exact(const uint8_t* in, const uint8_t* in_end, Uint* out,
101 int batch_size, int bit_offset) {
102 static_assert(kPackedBitWidth > 0);
103
104 // For the epilog we adapt the max spread since better alignment give shorter spreads
105 ARROW_DCHECK(kIsProlog || bit_offset == 0);
106 ARROW_DCHECK(bit_offset >= 0 && bit_offset < 8);
107 constexpr int kMaxSpreadBytes = kIsProlog ? PackedMaxSpreadBytes(kPackedBitWidth)
108 : PackedMaxSpreadBytes(kPackedBitWidth, 0);
109 using buffer_uint = SpreadBufferUint<kMaxSpreadBytes>;
110 constexpr int kBufferSize = sizeof(buffer_uint);
111 // Due to misalignment, on large bit width, the spread can be larger than the maximum
112 // size integer. For instance a 63 bit width misaligned packed integer can spread over 9
113 // aligned bytes.
114 constexpr bool kLarge = kBufferSize < kMaxSpreadBytes;
115 constexpr buffer_uint kLowMask =
116 bit_util::LeastSignificantBitMask<buffer_uint, true>(kPackedBitWidth);
117
118 ARROW_DCHECK_GE(bit_offset, 0);
119 ARROW_DCHECK_LE(bit_offset, 8);
120
121 // Looping over values one by one
122 const int start_bit_term = batch_size * kPackedBitWidth + bit_offset;
123 int start_bit = bit_offset;
124 while ((start_bit < start_bit_term) && (!kIsProlog || (start_bit % 8 != 0))) {
125 const int start_byte = start_bit / 8;
126 const int spread_bytes = ((start_bit + kPackedBitWidth - 1) / 8) - start_byte + 1;
127 ARROW_DCHECK_LE(spread_bytes, kMaxSpreadBytes);
128 ARROW_COMPILER_ASSUME(spread_bytes <= kMaxSpreadBytes);
129
130 // Reading the bytes for the current value.
131 buffer_uint buffer = 0;
132 if (ARROW_PREDICT_TRUE(in + start_byte + kBufferSize < in_end)) {
133 // Fast path we read the whole buffer. In all but few last reads (plural!) we will
134 // always have enough bytes left in the buffer to avoid out-of-bounds reads. On top
135 // of this, in Arrow, `unpack` is always called with `max_read_bytes` set, meaning
136 // this will often be the *only* path taken.
137 // We added this special case because `std::memcpy` without a compile time constant
138 // was not inlined and optimized properly by the compiler, resulting to a function
139 // call on each iteration.
140 // This also handles the `kLarge` case detailed below.
141 std::memcpy(&buffer, in + start_byte, kBufferSize);
142 } else {
143 // Slow path, we need to read exactly the correct number of bytes to avoid
144 // out-of-bounds reads.
145 if constexpr (kLarge) {
146 // We read the max possible bytes in the first pass and handle the rest after.
147 // Even though the worst spread does not happen on all iterations we can still
148 // read all bytes because we will mask them.
149 std::memcpy(&buffer, in + start_byte, std::min(kBufferSize, spread_bytes));
150 } else {
151 std::memcpy(&buffer, in + start_byte, spread_bytes);
152 }
153 }
154
155 buffer = bit_util::FromLittleEndian(buffer);
156 const int bit_offset = start_bit % 8;
157 buffer >>= bit_offset;

Callers

nothing calls this directly

Calls 2

PackedMaxSpreadBytesFunction · 0.85
FromLittleEndianFunction · 0.70

Tested by

no test coverage detected