akashchamp commented on code in PR #51310:
URL: https://github.com/apache/arrow/pull/51310#discussion_r4057267146


##########
cpp/src/arrow/util/bit_run_reader.h:
##########
@@ -466,6 +468,386 @@ inline uint64_t 
BaseSetBitRunReader<true>::ConsumeBits(uint64_t word, int32_t nu
 using SetBitRunReader = BaseSetBitRunReader</*Reverse=*/false>;
 using ReverseSetBitRunReader = BaseSetBitRunReader</*Reverse=*/true>;
 
+struct PositionedBitRun {
+  int64_t position;
+  int64_t length;
+  bool set;
+
+  std::string ToString() const {
+    return std::string("{pos=") + std::to_string(position) +
+           ", len=" + std::to_string(length) + ", set=" + std::to_string(set) 
+ "}";
+  }
+};
+
+inline bool operator==(const PositionedBitRun& lhs, const PositionedBitRun& 
rhs) {
+  return lhs.position == rhs.position && lhs.length == rhs.length && lhs.set 
== rhs.set;
+}
+
+inline bool operator!=(const PositionedBitRun& lhs, const PositionedBitRun& 
rhs) {
+  return lhs.position != rhs.position || lhs.length != rhs.length || lhs.set 
!= rhs.set;
+}
+
+/// \brief An input iterator over all contiguous bit runs in a bitmap range.
+class BitRunIterator {
+ public:
+  using iterator_category = std::input_iterator_tag;
+  using value_type = PositionedBitRun;
+  using difference_type = int64_t;
+  using pointer = const value_type*;
+  using reference = const value_type&;
+
+  BitRunIterator(const uint8_t* bitmap, int64_t offset, int64_t length)
+      : all_set_(bitmap == NULLPTR), at_end_(length == 0) {
+    if (at_end_) {
+      return;
+    }
+    if (all_set_) {
+      current_ = {0, length, true};
+      return;
+    }
+    reader_.emplace(bitmap, offset, length);
+    Advance();
+  }
+
+  reference operator*() const { return current_; }
+  pointer operator->() const { return &current_; }
+
+  BitRunIterator& operator++() {
+    if (!at_end_) {
+      if (all_set_) {
+        at_end_ = true;
+      } else {
+        Advance();
+      }
+    }
+    return *this;
+  }
+
+  BitRunIterator operator++(int) {
+    BitRunIterator copy = *this;
+    ++(*this);
+    return copy;
+  }
+
+  bool operator==(std::default_sentinel_t) const { return at_end_; }
+  bool operator!=(std::default_sentinel_t) const { return !at_end_; }
+  friend bool operator==(std::default_sentinel_t, const BitRunIterator& 
iterator) {
+    return iterator.at_end_;
+  }
+  friend bool operator!=(std::default_sentinel_t, const BitRunIterator& 
iterator) {
+    return !iterator.at_end_;
+  }

Review Comment:
   Dropped the instance methods. Each iterator now has a single friend 
operator==(const Iterator&, std::default_sentinel_t); since we are on C++20 the 
compiler synthesizes operator!= and the reversed-argument form from it, so both 
it != end and end != it in the tests still work.
   



##########
cpp/src/arrow/util/bit_run_reader.h:
##########
@@ -466,6 +468,386 @@ inline uint64_t 
BaseSetBitRunReader<true>::ConsumeBits(uint64_t word, int32_t nu
 using SetBitRunReader = BaseSetBitRunReader</*Reverse=*/false>;
 using ReverseSetBitRunReader = BaseSetBitRunReader</*Reverse=*/true>;
 
+struct PositionedBitRun {
+  int64_t position;
+  int64_t length;
+  bool set;
+
+  std::string ToString() const {
+    return std::string("{pos=") + std::to_string(position) +
+           ", len=" + std::to_string(length) + ", set=" + std::to_string(set) 
+ "}";
+  }
+};
+
+inline bool operator==(const PositionedBitRun& lhs, const PositionedBitRun& 
rhs) {
+  return lhs.position == rhs.position && lhs.length == rhs.length && lhs.set 
== rhs.set;
+}
+
+inline bool operator!=(const PositionedBitRun& lhs, const PositionedBitRun& 
rhs) {
+  return lhs.position != rhs.position || lhs.length != rhs.length || lhs.set 
!= rhs.set;
+}
+
+/// \brief An input iterator over all contiguous bit runs in a bitmap range.
+class BitRunIterator {
+ public:
+  using iterator_category = std::input_iterator_tag;
+  using value_type = PositionedBitRun;
+  using difference_type = int64_t;
+  using pointer = const value_type*;
+  using reference = const value_type&;
+
+  BitRunIterator(const uint8_t* bitmap, int64_t offset, int64_t length)
+      : all_set_(bitmap == NULLPTR), at_end_(length == 0) {
+    if (at_end_) {
+      return;
+    }
+    if (all_set_) {
+      current_ = {0, length, true};
+      return;
+    }
+    reader_.emplace(bitmap, offset, length);
+    Advance();
+  }
+
+  reference operator*() const { return current_; }
+  pointer operator->() const { return &current_; }
+
+  BitRunIterator& operator++() {
+    if (!at_end_) {
+      if (all_set_) {
+        at_end_ = true;
+      } else {
+        Advance();
+      }
+    }
+    return *this;
+  }
+
+  BitRunIterator operator++(int) {
+    BitRunIterator copy = *this;
+    ++(*this);
+    return copy;
+  }
+
+  bool operator==(std::default_sentinel_t) const { return at_end_; }
+  bool operator!=(std::default_sentinel_t) const { return !at_end_; }
+  friend bool operator==(std::default_sentinel_t, const BitRunIterator& 
iterator) {
+    return iterator.at_end_;
+  }
+  friend bool operator!=(std::default_sentinel_t, const BitRunIterator& 
iterator) {
+    return !iterator.at_end_;
+  }
+
+ private:
+  void Advance() {
+    const auto run = reader_->NextRun();
+    if (run.length == 0) {
+      at_end_ = true;
+      return;
+    }
+    current_ = {position_, run.length, run.set};
+    position_ += run.length;
+  }
+
+  std::optional<BitRunReader> reader_;
+  PositionedBitRun current_;
+  int64_t position_ = 0;
+  bool all_set_ = false;
+  bool at_end_ = false;
+};
+
+/// \brief A range over all contiguous bit runs in a bitmap range.
+class BitRunRange {
+ public:
+  BitRunRange(const uint8_t* bitmap, int64_t offset, int64_t length)
+      : bitmap_(bitmap), offset_(offset), length_(length) {}
+
+  BitRunIterator begin() const { return BitRunIterator(bitmap_, offset_, 
length_); }
+  std::default_sentinel_t end() const { return {}; }
+
+ private:
+  const uint8_t* bitmap_;
+  int64_t offset_;
+  int64_t length_;
+};
+
+inline BitRunRange IterateBitRuns(const uint8_t* bitmap, int64_t offset, 
int64_t length) {
+  return {bitmap, offset, length};
+}
+
+/// \brief An input iterator over contiguous set-bit runs in a bitmap range.
+class SetBitRunIterator {
+ public:
+  using iterator_category = std::input_iterator_tag;
+  using value_type = SetBitRun;
+  using difference_type = int64_t;
+  using pointer = const value_type*;
+  using reference = const value_type&;
+
+  SetBitRunIterator(const uint8_t* bitmap, int64_t offset, int64_t length)
+      : all_set_(bitmap == NULLPTR), at_end_(length == 0) {
+    if (at_end_) {
+      return;
+    }
+    if (all_set_) {
+      current_ = {0, length};
+      return;
+    }
+    reader_.emplace(bitmap, offset, length);
+    Advance();
+  }
+
+  SetBitRunIterator(const SetBitRunIterator&) = default;
+
+  SetBitRunIterator& operator=(const SetBitRunIterator& other) {
+    if (this != &other) {
+      reader_.reset();
+      if (other.reader_) {
+        reader_.emplace(*other.reader_);
+      }
+      current_ = other.current_;
+      all_set_ = other.all_set_;
+      at_end_ = other.at_end_;
+    }
+    return *this;
+  }
+
+  reference operator*() const { return current_; }
+  pointer operator->() const { return &current_; }
+
+  SetBitRunIterator& operator++() {
+    if (!at_end_) {
+      if (all_set_) {
+        at_end_ = true;
+      } else {
+        Advance();
+      }
+    }
+    return *this;
+  }
+
+  SetBitRunIterator operator++(int) {
+    SetBitRunIterator copy = *this;
+    ++(*this);
+    return copy;
+  }
+
+  bool operator==(std::default_sentinel_t) const { return at_end_; }
+  bool operator!=(std::default_sentinel_t) const { return !at_end_; }
+  friend bool operator==(std::default_sentinel_t, const SetBitRunIterator& 
iterator) {
+    return iterator.at_end_;
+  }
+  friend bool operator!=(std::default_sentinel_t, const SetBitRunIterator& 
iterator) {
+    return !iterator.at_end_;
+  }

Review Comment:
   Same change here: SetBitRunIterator and TwoSetBitRunIterator keep only the 
single friend operator== against std::default_sentinel_t and rely on the C++20 
rewrites for the rest.
   



##########
cpp/src/arrow/util/bit_run_reader.h:
##########
@@ -466,6 +468,386 @@ inline uint64_t 
BaseSetBitRunReader<true>::ConsumeBits(uint64_t word, int32_t nu
 using SetBitRunReader = BaseSetBitRunReader</*Reverse=*/false>;
 using ReverseSetBitRunReader = BaseSetBitRunReader</*Reverse=*/true>;
 
+struct PositionedBitRun {
+  int64_t position;
+  int64_t length;
+  bool set;
+
+  std::string ToString() const {
+    return std::string("{pos=") + std::to_string(position) +
+           ", len=" + std::to_string(length) + ", set=" + std::to_string(set) 
+ "}";
+  }
+};
+
+inline bool operator==(const PositionedBitRun& lhs, const PositionedBitRun& 
rhs) {
+  return lhs.position == rhs.position && lhs.length == rhs.length && lhs.set 
== rhs.set;
+}
+
+inline bool operator!=(const PositionedBitRun& lhs, const PositionedBitRun& 
rhs) {
+  return lhs.position != rhs.position || lhs.length != rhs.length || lhs.set 
!= rhs.set;
+}
+
+/// \brief An input iterator over all contiguous bit runs in a bitmap range.
+class BitRunIterator {
+ public:
+  using iterator_category = std::input_iterator_tag;
+  using value_type = PositionedBitRun;
+  using difference_type = int64_t;
+  using pointer = const value_type*;
+  using reference = const value_type&;
+
+  BitRunIterator(const uint8_t* bitmap, int64_t offset, int64_t length)
+      : all_set_(bitmap == NULLPTR), at_end_(length == 0) {
+    if (at_end_) {
+      return;
+    }
+    if (all_set_) {
+      current_ = {0, length, true};
+      return;
+    }
+    reader_.emplace(bitmap, offset, length);
+    Advance();
+  }
+
+  reference operator*() const { return current_; }
+  pointer operator->() const { return &current_; }
+
+  BitRunIterator& operator++() {
+    if (!at_end_) {
+      if (all_set_) {
+        at_end_ = true;
+      } else {
+        Advance();
+      }
+    }
+    return *this;
+  }
+
+  BitRunIterator operator++(int) {
+    BitRunIterator copy = *this;
+    ++(*this);
+    return copy;
+  }
+
+  bool operator==(std::default_sentinel_t) const { return at_end_; }
+  bool operator!=(std::default_sentinel_t) const { return !at_end_; }
+  friend bool operator==(std::default_sentinel_t, const BitRunIterator& 
iterator) {
+    return iterator.at_end_;
+  }
+  friend bool operator!=(std::default_sentinel_t, const BitRunIterator& 
iterator) {
+    return !iterator.at_end_;
+  }
+
+ private:
+  void Advance() {
+    const auto run = reader_->NextRun();
+    if (run.length == 0) {
+      at_end_ = true;
+      return;
+    }
+    current_ = {position_, run.length, run.set};
+    position_ += run.length;
+  }
+
+  std::optional<BitRunReader> reader_;
+  PositionedBitRun current_;
+  int64_t position_ = 0;
+  bool all_set_ = false;
+  bool at_end_ = false;
+};
+
+/// \brief A range over all contiguous bit runs in a bitmap range.
+class BitRunRange {
+ public:
+  BitRunRange(const uint8_t* bitmap, int64_t offset, int64_t length)
+      : bitmap_(bitmap), offset_(offset), length_(length) {}
+
+  BitRunIterator begin() const { return BitRunIterator(bitmap_, offset_, 
length_); }
+  std::default_sentinel_t end() const { return {}; }
+
+ private:
+  const uint8_t* bitmap_;
+  int64_t offset_;
+  int64_t length_;
+};
+
+inline BitRunRange IterateBitRuns(const uint8_t* bitmap, int64_t offset, 
int64_t length) {
+  return {bitmap, offset, length};
+}
+
+/// \brief An input iterator over contiguous set-bit runs in a bitmap range.
+class SetBitRunIterator {
+ public:
+  using iterator_category = std::input_iterator_tag;
+  using value_type = SetBitRun;
+  using difference_type = int64_t;
+  using pointer = const value_type*;
+  using reference = const value_type&;
+
+  SetBitRunIterator(const uint8_t* bitmap, int64_t offset, int64_t length)
+      : all_set_(bitmap == NULLPTR), at_end_(length == 0) {
+    if (at_end_) {
+      return;
+    }
+    if (all_set_) {
+      current_ = {0, length};
+      return;
+    }
+    reader_.emplace(bitmap, offset, length);
+    Advance();
+  }
+
+  SetBitRunIterator(const SetBitRunIterator&) = default;
+
+  SetBitRunIterator& operator=(const SetBitRunIterator& other) {
+    if (this != &other) {
+      reader_.reset();
+      if (other.reader_) {
+        reader_.emplace(*other.reader_);
+      }
+      current_ = other.current_;
+      all_set_ = other.all_set_;
+      at_end_ = other.at_end_;
+    }
+    return *this;
+  }
+
+  reference operator*() const { return current_; }
+  pointer operator->() const { return &current_; }
+
+  SetBitRunIterator& operator++() {
+    if (!at_end_) {
+      if (all_set_) {
+        at_end_ = true;
+      } else {
+        Advance();
+      }
+    }
+    return *this;
+  }
+
+  SetBitRunIterator operator++(int) {
+    SetBitRunIterator copy = *this;
+    ++(*this);
+    return copy;
+  }
+
+  bool operator==(std::default_sentinel_t) const { return at_end_; }
+  bool operator!=(std::default_sentinel_t) const { return !at_end_; }
+  friend bool operator==(std::default_sentinel_t, const SetBitRunIterator& 
iterator) {
+    return iterator.at_end_;
+  }
+  friend bool operator!=(std::default_sentinel_t, const SetBitRunIterator& 
iterator) {
+    return !iterator.at_end_;
+  }
+
+ private:
+  void Advance() {
+    current_ = reader_->NextRun();
+    at_end_ = current_.AtEnd();
+  }
+
+  std::optional<SetBitRunReader> reader_;
+  SetBitRun current_{};
+  bool all_set_ = false;
+  bool at_end_ = false;
+};
+
+/// \brief A range over contiguous set-bit runs in a bitmap range.
+class SetBitRunRange {
+ public:
+  SetBitRunRange(const uint8_t* bitmap, int64_t offset, int64_t length)
+      : bitmap_(bitmap), offset_(offset), length_(length) {}
+
+  SetBitRunIterator begin() const { return SetBitRunIterator(bitmap_, offset_, 
length_); }
+  std::default_sentinel_t end() const { return {}; }
+
+ private:
+  const uint8_t* bitmap_;
+  int64_t offset_;
+  int64_t length_;
+};
+
+inline SetBitRunRange IterateSetBitRuns(const uint8_t* bitmap, int64_t offset,
+                                        int64_t length) {
+  return {bitmap, offset, length};
+}
+
+/// \brief An input iterator over set-bit runs in the intersection of two 
bitmap ranges.
+class TwoSetBitRunIterator {
+ public:
+  using iterator_category = std::input_iterator_tag;
+  using value_type = SetBitRun;
+  using difference_type = int64_t;
+  using pointer = const value_type*;
+  using reference = const value_type&;
+
+  TwoSetBitRunIterator(const uint8_t* left_bitmap, int64_t left_offset,
+                       const uint8_t* right_bitmap, int64_t right_offset, 
int64_t length)
+      : at_end_(length == 0) {
+    if (at_end_) {
+      return;
+    }
+    if (left_bitmap == NULLPTR && right_bitmap == NULLPTR) {
+      mode_ = Mode::kAllSet;
+      current_ = {0, length};
+      return;
+    }
+    if (left_bitmap == NULLPTR || right_bitmap == NULLPTR) {
+      mode_ = Mode::kSingleBitmap;
+      if (left_bitmap == NULLPTR) {
+        single_reader_.emplace(right_bitmap, right_offset, length);
+      } else {
+        single_reader_.emplace(left_bitmap, left_offset, length);
+      }
+      AdvanceSingleBitmap();
+      return;
+    }
+
+    left_reader_.emplace(left_bitmap, left_offset, length);
+    right_reader_.emplace(right_bitmap, right_offset, length);
+    left_run_ = left_reader_->NextRun();
+    right_run_ = right_reader_->NextRun();
+    AdvanceTwoBitmaps();
+  }
+
+  TwoSetBitRunIterator(const TwoSetBitRunIterator&) = default;
+
+  TwoSetBitRunIterator& operator=(const TwoSetBitRunIterator& other) {

Review Comment:
   It does not need to be copyable as such, but std::input_iterator requires 
std::movable, and the hand-written operator= only existed because 
BaseSetBitRunReader::length_ was const, which made 
std::optional<SetBitRunReader> neither copy- nor move-assignable. I dropped 
that const instead, so the compiler now generates all the special members and 
the custom copy constructor, assignment and CopyReader helper are gone; the 
iterator stays a plain copyable value type like BitRunIterator, which postfix 
++ needs anyway.
   



##########
cpp/src/arrow/util/bit_run_reader.h:
##########
@@ -466,6 +468,386 @@ inline uint64_t 
BaseSetBitRunReader<true>::ConsumeBits(uint64_t word, int32_t nu
 using SetBitRunReader = BaseSetBitRunReader</*Reverse=*/false>;
 using ReverseSetBitRunReader = BaseSetBitRunReader</*Reverse=*/true>;
 
+struct PositionedBitRun {
+  int64_t position;
+  int64_t length;
+  bool set;
+
+  std::string ToString() const {
+    return std::string("{pos=") + std::to_string(position) +
+           ", len=" + std::to_string(length) + ", set=" + std::to_string(set) 
+ "}";
+  }
+};
+
+inline bool operator==(const PositionedBitRun& lhs, const PositionedBitRun& 
rhs) {
+  return lhs.position == rhs.position && lhs.length == rhs.length && lhs.set 
== rhs.set;
+}
+
+inline bool operator!=(const PositionedBitRun& lhs, const PositionedBitRun& 
rhs) {
+  return lhs.position != rhs.position || lhs.length != rhs.length || lhs.set 
!= rhs.set;
+}
+
+/// \brief An input iterator over all contiguous bit runs in a bitmap range.
+class BitRunIterator {
+ public:
+  using iterator_category = std::input_iterator_tag;
+  using value_type = PositionedBitRun;
+  using difference_type = int64_t;
+  using pointer = const value_type*;
+  using reference = const value_type&;
+
+  BitRunIterator(const uint8_t* bitmap, int64_t offset, int64_t length)
+      : all_set_(bitmap == NULLPTR), at_end_(length == 0) {
+    if (at_end_) {
+      return;
+    }
+    if (all_set_) {
+      current_ = {0, length, true};
+      return;
+    }
+    reader_.emplace(bitmap, offset, length);
+    Advance();
+  }
+
+  reference operator*() const { return current_; }
+  pointer operator->() const { return &current_; }
+
+  BitRunIterator& operator++() {
+    if (!at_end_) {
+      if (all_set_) {
+        at_end_ = true;
+      } else {
+        Advance();
+      }
+    }
+    return *this;
+  }
+
+  BitRunIterator operator++(int) {
+    BitRunIterator copy = *this;
+    ++(*this);
+    return copy;
+  }
+
+  bool operator==(std::default_sentinel_t) const { return at_end_; }
+  bool operator!=(std::default_sentinel_t) const { return !at_end_; }
+  friend bool operator==(std::default_sentinel_t, const BitRunIterator& 
iterator) {
+    return iterator.at_end_;
+  }
+  friend bool operator!=(std::default_sentinel_t, const BitRunIterator& 
iterator) {
+    return !iterator.at_end_;
+  }
+
+ private:
+  void Advance() {
+    const auto run = reader_->NextRun();
+    if (run.length == 0) {
+      at_end_ = true;
+      return;
+    }
+    current_ = {position_, run.length, run.set};
+    position_ += run.length;
+  }
+
+  std::optional<BitRunReader> reader_;
+  PositionedBitRun current_;
+  int64_t position_ = 0;
+  bool all_set_ = false;
+  bool at_end_ = false;
+};
+
+/// \brief A range over all contiguous bit runs in a bitmap range.
+class BitRunRange {
+ public:
+  BitRunRange(const uint8_t* bitmap, int64_t offset, int64_t length)
+      : bitmap_(bitmap), offset_(offset), length_(length) {}
+
+  BitRunIterator begin() const { return BitRunIterator(bitmap_, offset_, 
length_); }
+  std::default_sentinel_t end() const { return {}; }
+
+ private:
+  const uint8_t* bitmap_;
+  int64_t offset_;
+  int64_t length_;
+};
+
+inline BitRunRange IterateBitRuns(const uint8_t* bitmap, int64_t offset, 
int64_t length) {
+  return {bitmap, offset, length};
+}
+
+/// \brief An input iterator over contiguous set-bit runs in a bitmap range.
+class SetBitRunIterator {
+ public:
+  using iterator_category = std::input_iterator_tag;
+  using value_type = SetBitRun;
+  using difference_type = int64_t;
+  using pointer = const value_type*;
+  using reference = const value_type&;
+
+  SetBitRunIterator(const uint8_t* bitmap, int64_t offset, int64_t length)
+      : all_set_(bitmap == NULLPTR), at_end_(length == 0) {
+    if (at_end_) {
+      return;
+    }
+    if (all_set_) {
+      current_ = {0, length};
+      return;
+    }
+    reader_.emplace(bitmap, offset, length);
+    Advance();
+  }
+
+  SetBitRunIterator(const SetBitRunIterator&) = default;
+
+  SetBitRunIterator& operator=(const SetBitRunIterator& other) {

Review Comment:
   Same answer as for TwoSetBitRunIterator: the custom copy assignment was only 
working around the const length_ in BaseSetBitRunReader. With that const 
removed the implicitly defaulted members do the right thing, so I deleted the 
hand-written ones here too.
   



##########
cpp/src/arrow/util/bitmap_test.cc:
##########
@@ -607,6 +613,156 @@ TEST_F(TestSetBitRunReader, VisitTwoBitRunsNoOverlap) {
   ASSERT_THAT(runs, ElementsAreArray({BitRun{8, false}}));
 }
 
+TEST_F(TestSetBitRunReader, IterateBitRuns) {
+  auto bitmap = BitmapFromString("01101101");
+  const auto range = IterateBitRuns(bitmap->data(), /*offset=*/1, 
/*length=*/6);
+
+  std::vector<PositionedBitRun> runs;
+  for (const auto run : range) {
+    runs.push_back(run);
+  }
+  ASSERT_THAT(runs, ElementsAreArray(std::vector<PositionedBitRun>{
+                        {0, 2, true}, {2, 1, false}, {3, 2, true}, {5, 1, 
false}}));
+
+  std::vector<PositionedBitRun> null_bitmap_runs;
+  for (const auto run : IterateBitRuns(nullptr, /*offset=*/12, /*length=*/6)) {
+    null_bitmap_runs.push_back(run);
+  }
+  ASSERT_THAT(null_bitmap_runs,
+              ElementsAreArray(std::vector<PositionedBitRun>{{0, 6, true}}));
+
+  std::vector<PositionedBitRun> empty_runs;
+  for (const auto run : IterateBitRuns(nullptr, /*offset=*/0, /*length=*/0)) {
+    empty_runs.push_back(run);
+  }
+  EXPECT_TRUE(empty_runs.empty());
+
+  int run_count = 0;
+  for (const auto run : range) {
+    EXPECT_EQ(run.position, 0);
+    ++run_count;
+    break;
+  }

Review Comment:
   It was only checking that a range-for over the range can break after the 
first run, which is a language property rather than iterator behavior, and the 
range.begin() checks right below already verify that a fresh iteration starts 
at position 0. I removed the block.
   



##########
cpp/src/arrow/util/bit_run_reader.h:
##########
@@ -466,6 +468,386 @@ inline uint64_t 
BaseSetBitRunReader<true>::ConsumeBits(uint64_t word, int32_t nu
 using SetBitRunReader = BaseSetBitRunReader</*Reverse=*/false>;
 using ReverseSetBitRunReader = BaseSetBitRunReader</*Reverse=*/true>;
 
+struct PositionedBitRun {
+  int64_t position;
+  int64_t length;
+  bool set;
+
+  std::string ToString() const {
+    return std::string("{pos=") + std::to_string(position) +
+           ", len=" + std::to_string(length) + ", set=" + std::to_string(set) 
+ "}";
+  }
+};
+
+inline bool operator==(const PositionedBitRun& lhs, const PositionedBitRun& 
rhs) {

Review Comment:
   Done: PositionedBitRun now has bool operator==(const PositionedBitRun&) 
const = default; and the two free operators are gone. Arrow already requires 
C++20 (SetupCxxFlags.cmake sets CMAKE_CXX_STANDARD 20 and the docs ask for gcc 
12 or newer), and I checked both == and the synthesized != in a small 
standalone C++20 translation unit, so the CI compilers should all be fine with 
it.
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to