Skip to content

Commit 56d3e20

Browse files
committed
Add BitPackedRunToBitMapDecoder
1 parent 36d9e06 commit 56d3e20

2 files changed

Lines changed: 386 additions & 45 deletions

File tree

cpp/src/arrow/util/rle_bitmap_internal.h

Lines changed: 107 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,8 @@ class RunToBitMapDecoderMixin {
110110
const auto n_vals = derived()->GetBatchFast(out, batch_size);
111111

112112
// TRAILER: Writing inside the last byte if caller asked for non multiple of 8 values
113-
const auto n_last_vals = batch_size - n_vals;
114-
if (ARROW_PREDICT_FALSE(derived()->remaining() > 0 && n_last_vals > 0)) {
115-
ARROW_DCHECK_GT(n_last_vals, 0);
113+
const auto n_last_vals = std::min(batch_size - n_vals, derived()->remaining());
114+
if (ARROW_PREDICT_FALSE(n_last_vals > 0)) {
116115
ARROW_DCHECK_LT(n_last_vals, 8);
117116
out = out.NewStartingAt(n_vals);
118117
return n_vals + derived()->GetBatchFirstByte(out, n_last_vals);
@@ -323,4 +322,109 @@ class BitPackedRunToBitMapDecoder
323322
}
324323
};
325324

325+
/// A specialized decoder class to extract RLE+bitpacked booleans.
326+
///
327+
/// In some cases, such as when reading definition levels for nullable values (with
328+
/// no repetition and no nesting), we know values to be decoded will end up in an
329+
/// Arrow validity bitmap. In such cases, decoding values to a ``int16`` before
330+
/// encoding them again in overly wasteful.
331+
class RleBitPackedToBitMapDecoder {
332+
public:
333+
RleBitPackedToBitMapDecoder() noexcept = default;
334+
335+
/// Create a decoder object.
336+
///
337+
/// data and data_size are the raw bytes to decode.
338+
RleBitPackedToBitMapDecoder(const uint8_t* data, rle_size_t data_size) noexcept {
339+
Reset(data, data_size);
340+
}
341+
342+
void Reset(const uint8_t* data, rle_size_t data_size) noexcept {
343+
parser_.Reset(data, data_size, /* value_bit_width= */ 1);
344+
decoder_ = {};
345+
}
346+
347+
/// Whether there is still runs to iterate over.
348+
bool exhausted() const { return (run_remaining() == 0) && parser_.exhausted(); }
349+
350+
/// Get a batch of values return the number of decoded elements.
351+
/// May write fewer elements to the output than requested if there are not enough
352+
/// values left or if an error occurred.
353+
[[nodiscard]] rle_size_t GetBatch(BitmapSpanMut out, rle_size_t batch_size);
354+
355+
private:
356+
/// Utility to map a run type to the associate decoder.
357+
template <typename Run>
358+
struct get_decoder;
359+
template <>
360+
struct get_decoder<RleRun> {
361+
using type = RleRunToBitMapDecoder;
362+
};
363+
template <>
364+
struct get_decoder<BitPackedRun> {
365+
using type = BitPackedRunToBitMapDecoder;
366+
};
367+
template <typename Run>
368+
using get_decoder_t = get_decoder<Run>::type;
369+
370+
RleBitPackedParser parser_ = {};
371+
std::variant<RleRunToBitMapDecoder, BitPackedRunToBitMapDecoder> decoder_ = {};
372+
373+
/// Return the number of values that are remaining in the current run.
374+
rle_size_t run_remaining() const {
375+
return std::visit([](const auto& dec) { return dec.remaining(); }, decoder_);
376+
}
377+
378+
/// Get a batch of values from the current run and return the number elements read.
379+
[[nodiscard]] rle_size_t RunGetBatch(BitmapSpanMut out, rle_size_t batch_size) {
380+
return std::visit([&](auto& dec) { return dec.GetBatch(out, batch_size); }, decoder_);
381+
}
382+
};
383+
384+
/************************************************
385+
* RleBitPackedToBitMapDecoder implementation *
386+
************************************************/
387+
388+
inline auto RleBitPackedToBitMapDecoder::GetBatch(BitmapSpanMut out,
389+
rle_size_t batch_size) -> rle_size_t {
390+
using ControlFlow = RleBitPackedParser::ControlFlow;
391+
392+
rle_size_t values_read = 0;
393+
394+
// Remaining from a previous call that would have left some unread data from a run.
395+
if (ARROW_PREDICT_FALSE(run_remaining() > 0)) {
396+
const auto read = RunGetBatch(out, batch_size);
397+
values_read += read;
398+
399+
// Either we fulfilled all the batch to be read or we finished remaining run.
400+
if (ARROW_PREDICT_FALSE(values_read == batch_size)) {
401+
return values_read;
402+
}
403+
ARROW_DCHECK(run_remaining() == 0);
404+
}
405+
406+
parser_.ParseWithCallable([&](auto run) {
407+
using RunDecoder = get_decoder_t<decltype(run)>;
408+
409+
ARROW_DCHECK_LT(values_read, batch_size);
410+
RunDecoder decoder(run);
411+
// The output span carries its own bit offset, so advancing it past the values
412+
// already written keeps successive runs correctly aligned in the bitmap.
413+
const auto read =
414+
decoder.GetBatch(out.NewStartingAt(values_read), batch_size - values_read);
415+
ARROW_DCHECK_LE(read, batch_size - values_read);
416+
values_read += read;
417+
418+
// Stop reading and store remaining decoder
419+
if (ARROW_PREDICT_FALSE(values_read == batch_size || read == 0)) {
420+
decoder_ = std::move(decoder);
421+
return ControlFlow::Break;
422+
}
423+
424+
return ControlFlow::Continue;
425+
});
426+
427+
return values_read;
428+
}
429+
326430
} // namespace arrow::util

0 commit comments

Comments
 (0)