Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-08-28 08:27:03

0001 // Licensed to the Apache Software Foundation (ASF) under one
0002 // or more contributor license agreements.  See the NOTICE file
0003 // distributed with this work for additional information
0004 // regarding copyright ownership.  The ASF licenses this file
0005 // to you under the Apache License, Version 2.0 (the
0006 // "License"); you may not use this file except in compliance
0007 // with the License.  You may obtain a copy of the License at
0008 //
0009 //   http://www.apache.org/licenses/LICENSE-2.0
0010 //
0011 // Unless required by applicable law or agreed to in writing,
0012 // software distributed under the License is distributed on an
0013 // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
0014 // KIND, either express or implied.  See the License for the
0015 // specific language governing permissions and limitations
0016 // under the License.
0017 
0018 #pragma once
0019 
0020 #include <algorithm>
0021 #include <array>
0022 #include <bitset>
0023 #include <cassert>
0024 #include <cstdint>
0025 #include <cstring>
0026 #include <memory>
0027 #include <string>
0028 #include <string_view>
0029 #include <utility>
0030 
0031 #include "arrow/buffer.h"
0032 #include "arrow/util/bit_util.h"
0033 #include "arrow/util/bitmap_ops.h"
0034 #include "arrow/util/bitmap_reader.h"
0035 #include "arrow/util/bitmap_writer.h"
0036 #include "arrow/util/compare.h"
0037 #include "arrow/util/endian.h"
0038 #include "arrow/util/functional.h"
0039 #include "arrow/util/span.h"
0040 #include "arrow/util/string_builder.h"
0041 #include "arrow/util/visibility.h"
0042 
0043 namespace arrow {
0044 
0045 class BooleanArray;
0046 
0047 namespace internal {
0048 
0049 class ARROW_EXPORT Bitmap : public util::ToStringOstreamable<Bitmap>,
0050                             public util::EqualityComparable<Bitmap> {
0051  public:
0052   Bitmap() = default;
0053 
0054   Bitmap(const std::shared_ptr<Buffer>& buffer, int64_t offset, int64_t length)
0055       : data_(buffer->data()), offset_(offset), length_(length) {
0056     if (buffer->is_mutable()) {
0057       mutable_data_ = buffer->mutable_data();
0058     }
0059   }
0060 
0061   Bitmap(const void* data, int64_t offset, int64_t length)
0062       : data_(reinterpret_cast<const uint8_t*>(data)), offset_(offset), length_(length) {}
0063 
0064   Bitmap(void* data, int64_t offset, int64_t length)
0065       : data_(reinterpret_cast<const uint8_t*>(data)),
0066         mutable_data_(reinterpret_cast<uint8_t*>(data)),
0067         offset_(offset),
0068         length_(length) {}
0069 
0070   Bitmap Slice(int64_t offset) const {
0071     if (mutable_data_ != NULLPTR) {
0072       return {mutable_data_, offset_ + offset, length_ - offset};
0073     } else {
0074       return {data_, offset_ + offset, length_ - offset};
0075     }
0076   }
0077 
0078   Bitmap Slice(int64_t offset, int64_t length) const {
0079     if (mutable_data_ != NULLPTR) {
0080       return {mutable_data_, offset_ + offset, length};
0081     } else {
0082       return {data_, offset_ + offset, length};
0083     }
0084   }
0085 
0086   std::string ToString() const;
0087 
0088   bool Equals(const Bitmap& other) const;
0089 
0090   std::string Diff(const Bitmap& other) const;
0091 
0092   bool GetBit(int64_t i) const { return bit_util::GetBit(data_, i + offset_); }
0093 
0094   bool operator[](int64_t i) const { return GetBit(i); }
0095 
0096   void SetBitTo(int64_t i, bool v) const {
0097     bit_util::SetBitTo(mutable_data_, i + offset_, v);
0098   }
0099 
0100   void SetBitsTo(bool v) { bit_util::SetBitsTo(mutable_data_, offset_, length_, v); }
0101 
0102   void CopyFrom(const Bitmap& other);
0103   void CopyFromInverted(const Bitmap& other);
0104 
0105   /// \brief Visit bits from each bitmap as bitset<N>
0106   ///
0107   /// All bitmaps must have identical length.
0108   template <size_t N, typename Visitor>
0109   static void VisitBits(const Bitmap (&bitmaps)[N], Visitor&& visitor) {
0110     int64_t bit_length = BitLength(bitmaps, N);
0111     std::bitset<N> bits;
0112     for (int64_t bit_i = 0; bit_i < bit_length; ++bit_i) {
0113       for (size_t i = 0; i < N; ++i) {
0114         bits[i] = bitmaps[i].GetBit(bit_i);
0115       }
0116       visitor(bits);
0117     }
0118   }
0119 
0120   /// \brief Visit bits from each bitmap as bitset<N>
0121   ///
0122   /// All bitmaps must have identical length.
0123   template <size_t N, typename Visitor>
0124   static void VisitBits(const std::array<Bitmap, N>& bitmaps, Visitor&& visitor) {
0125     int64_t bit_length = BitLength(bitmaps);
0126     std::bitset<N> bits;
0127     for (int64_t bit_i = 0; bit_i < bit_length; ++bit_i) {
0128       for (size_t i = 0; i < N; ++i) {
0129         bits[i] = bitmaps[i].GetBit(bit_i);
0130       }
0131       visitor(bits);
0132     }
0133   }
0134 
0135   /// \brief Visit words of bits from each bitmap as array<Word, N>
0136   ///
0137   /// All bitmaps must have identical length. The first bit in a visited bitmap
0138   /// may be offset within the first visited word, but words will otherwise contain
0139   /// densely packed bits loaded from the bitmap. That offset within the first word is
0140   /// returned.
0141   ///
0142   /// TODO(bkietz) allow for early termination
0143   // NOTE: this function is efficient on 3+ sufficiently large bitmaps.
0144   // It also has a large prolog / epilog overhead and should be used
0145   // carefully in other cases.
0146   // For 2 bitmaps or less, and/or smaller bitmaps, see also VisitTwoBitBlocksVoid
0147   // and BitmapUInt64Reader.
0148   template <size_t N, typename Visitor,
0149             typename Word = typename std::decay<
0150                 internal::call_traits::argument_type<0, Visitor&&>>::type::value_type>
0151   static int64_t VisitWords(const Bitmap (&bitmaps_arg)[N], Visitor&& visitor) {
0152     constexpr int64_t kBitWidth = sizeof(Word) * 8;
0153 
0154     // local, mutable variables which will be sliced/decremented to represent consumption:
0155     Bitmap bitmaps[N];
0156     int64_t offsets[N];
0157     int64_t bit_length = BitLength(bitmaps_arg, N);
0158     util::span<const Word> words[N];
0159     for (size_t i = 0; i < N; ++i) {
0160       bitmaps[i] = bitmaps_arg[i];
0161       offsets[i] = bitmaps[i].template word_offset<Word>();
0162       assert(offsets[i] >= 0 && offsets[i] < kBitWidth);
0163       words[i] = bitmaps[i].template words<Word>();
0164     }
0165 
0166     auto consume = [&](int64_t consumed_bits) {
0167       for (size_t i = 0; i < N; ++i) {
0168         bitmaps[i] = bitmaps[i].Slice(consumed_bits, bit_length - consumed_bits);
0169         offsets[i] = bitmaps[i].template word_offset<Word>();
0170         assert(offsets[i] >= 0 && offsets[i] < kBitWidth);
0171         words[i] = bitmaps[i].template words<Word>();
0172       }
0173       bit_length -= consumed_bits;
0174     };
0175 
0176     std::array<Word, N> visited_words;
0177     visited_words.fill(0);
0178 
0179     if (bit_length <= kBitWidth * 2) {
0180       // bitmaps fit into one or two words so don't bother with optimization
0181       while (bit_length > 0) {
0182         auto leading_bits = std::min(bit_length, kBitWidth);
0183         SafeLoadWords(bitmaps, 0, leading_bits, false, &visited_words);
0184         visitor(visited_words);
0185         consume(leading_bits);
0186       }
0187       return 0;
0188     }
0189 
0190     int64_t max_offset = *std::max_element(offsets, offsets + N);
0191     int64_t min_offset = *std::min_element(offsets, offsets + N);
0192     if (max_offset > 0) {
0193       // consume leading bits
0194       auto leading_bits = kBitWidth - min_offset;
0195       SafeLoadWords(bitmaps, 0, leading_bits, true, &visited_words);
0196       visitor(visited_words);
0197       consume(leading_bits);
0198     }
0199     assert(*std::min_element(offsets, offsets + N) == 0);
0200 
0201     int64_t whole_word_count = bit_length / kBitWidth;
0202     assert(whole_word_count >= 1);
0203 
0204     if (min_offset == max_offset) {
0205       // all offsets were identical, all leading bits have been consumed
0206       assert(
0207           std::all_of(offsets, offsets + N, [](int64_t offset) { return offset == 0; }));
0208 
0209       for (int64_t word_i = 0; word_i < whole_word_count; ++word_i) {
0210         for (size_t i = 0; i < N; ++i) {
0211           visited_words[i] = words[i][word_i];
0212         }
0213         visitor(visited_words);
0214       }
0215       consume(whole_word_count * kBitWidth);
0216     } else {
0217       // leading bits from potentially incomplete words have been consumed
0218 
0219       // word_i such that words[i][word_i] and words[i][word_i + 1] are lie entirely
0220       // within the bitmap for all i
0221       for (int64_t word_i = 0; word_i < whole_word_count - 1; ++word_i) {
0222         for (size_t i = 0; i < N; ++i) {
0223           if (offsets[i] == 0) {
0224             visited_words[i] = words[i][word_i];
0225           } else {
0226             auto words0 = bit_util::ToLittleEndian(words[i][word_i]);
0227             auto words1 = bit_util::ToLittleEndian(words[i][word_i + 1]);
0228             visited_words[i] = bit_util::FromLittleEndian(
0229                 (words0 >> offsets[i]) | (words1 << (kBitWidth - offsets[i])));
0230           }
0231         }
0232         visitor(visited_words);
0233       }
0234       consume((whole_word_count - 1) * kBitWidth);
0235 
0236       SafeLoadWords(bitmaps, 0, kBitWidth, false, &visited_words);
0237 
0238       visitor(visited_words);
0239       consume(kBitWidth);
0240     }
0241 
0242     // load remaining bits
0243     if (bit_length > 0) {
0244       SafeLoadWords(bitmaps, 0, bit_length, false, &visited_words);
0245       visitor(visited_words);
0246     }
0247 
0248     return min_offset;
0249   }
0250 
0251   template <size_t N, size_t M, typename ReaderT, typename WriterT, typename Visitor,
0252             typename Word = typename std::decay<
0253                 internal::call_traits::argument_type<0, Visitor&&>>::type::value_type>
0254   static void RunVisitWordsAndWriteLoop(int64_t bit_length,
0255                                         std::array<ReaderT, N>& readers,
0256                                         std::array<WriterT, M>& writers,
0257                                         Visitor&& visitor) {
0258     constexpr int64_t kBitWidth = sizeof(Word) * 8;
0259 
0260     std::array<Word, N> visited_words;
0261     std::array<Word, M> output_words;
0262 
0263     // every reader will have same number of words, since they are same length'ed
0264     // TODO($JIRA) this will be inefficient in some cases. When there are offsets beyond
0265     //  Word boundary, every Word would have to be created from 2 adjoining Words
0266     auto n_words = readers[0].words();
0267     bit_length -= n_words * kBitWidth;
0268     while (n_words--) {
0269       // first collect all words to visited_words array
0270       for (size_t i = 0; i < N; i++) {
0271         visited_words[i] = readers[i].NextWord();
0272       }
0273       visitor(visited_words, &output_words);
0274       for (size_t i = 0; i < M; i++) {
0275         writers[i].PutNextWord(output_words[i]);
0276       }
0277     }
0278 
0279     // every reader will have same number of trailing bytes, because of the above reason
0280     // tailing portion could be more than one word! (ref: BitmapWordReader constructor)
0281     // remaining full/ partial words to write
0282 
0283     if (bit_length) {
0284       // convert the word visitor lambda to a byte_visitor
0285       auto byte_visitor = [&](const std::array<uint8_t, N>& in,
0286                               std::array<uint8_t, M>* out) {
0287         std::array<Word, N> in_words;
0288         std::array<Word, M> out_words;
0289         std::copy(in.begin(), in.end(), in_words.begin());
0290         visitor(in_words, &out_words);
0291         for (size_t i = 0; i < M; i++) {
0292           out->at(i) = static_cast<uint8_t>(out_words[i]);
0293         }
0294       };
0295 
0296       std::array<uint8_t, N> visited_bytes;
0297       std::array<uint8_t, M> output_bytes;
0298       int n_bytes = readers[0].trailing_bytes();
0299       while (n_bytes--) {
0300         visited_bytes.fill(0);
0301         output_bytes.fill(0);
0302         int valid_bits;
0303         for (size_t i = 0; i < N; i++) {
0304           visited_bytes[i] = readers[i].NextTrailingByte(valid_bits);
0305         }
0306         byte_visitor(visited_bytes, &output_bytes);
0307         for (size_t i = 0; i < M; i++) {
0308           writers[i].PutNextTrailingByte(output_bytes[i], valid_bits);
0309         }
0310       }
0311     }
0312   }
0313 
0314   /// \brief Visit words of bits from each input bitmap as array<Word, N> and collects
0315   /// outputs to an array<Word, M>, to be written into the output bitmaps accordingly.
0316   ///
0317   /// All bitmaps must have identical length. The first bit in a visited bitmap
0318   /// may be offset within the first visited word, but words will otherwise contain
0319   /// densely packed bits loaded from the bitmap. That offset within the first word is
0320   /// returned.
0321   /// Visitor is expected to have the following signature
0322   ///     [](const std::array<Word, N>& in_words, std::array<Word, M>* out_words){...}
0323   ///
0324   // NOTE: this function is efficient on 3+ sufficiently large bitmaps.
0325   // It also has a large prolog / epilog overhead and should be used
0326   // carefully in other cases.
0327   // For 2 bitmaps or less, and/or smaller bitmaps, see also VisitTwoBitBlocksVoid
0328   // and BitmapUInt64Reader.
0329   template <size_t N, size_t M, typename Visitor,
0330             typename Word = typename std::decay<
0331                 internal::call_traits::argument_type<0, Visitor&&>>::type::value_type>
0332   static void VisitWordsAndWrite(const std::array<Bitmap, N>& bitmaps_arg,
0333                                  std::array<Bitmap, M>* out_bitmaps_arg,
0334                                  Visitor&& visitor) {
0335     int64_t bit_length = BitLength(bitmaps_arg);
0336     assert(bit_length == BitLength(*out_bitmaps_arg));
0337 
0338     // if both input and output bitmaps have no byte offset, then use special template
0339     if (std::all_of(bitmaps_arg.begin(), bitmaps_arg.end(),
0340                     [](const Bitmap& b) { return b.offset_ % 8 == 0; }) &&
0341         std::all_of(out_bitmaps_arg->begin(), out_bitmaps_arg->end(),
0342                     [](const Bitmap& b) { return b.offset_ % 8 == 0; })) {
0343       std::array<BitmapWordReader<Word, /*may_have_byte_offset=*/false>, N> readers;
0344       for (size_t i = 0; i < N; ++i) {
0345         const Bitmap& in_bitmap = bitmaps_arg[i];
0346         readers[i] = BitmapWordReader<Word, /*may_have_byte_offset=*/false>(
0347             in_bitmap.data_, in_bitmap.offset_, in_bitmap.length_);
0348       }
0349 
0350       std::array<BitmapWordWriter<Word, /*may_have_byte_offset=*/false>, M> writers;
0351       for (size_t i = 0; i < M; ++i) {
0352         const Bitmap& out_bitmap = out_bitmaps_arg->at(i);
0353         writers[i] = BitmapWordWriter<Word, /*may_have_byte_offset=*/false>(
0354             out_bitmap.mutable_data_, out_bitmap.offset_, out_bitmap.length_);
0355       }
0356 
0357       RunVisitWordsAndWriteLoop(bit_length, readers, writers, visitor);
0358     } else {
0359       std::array<BitmapWordReader<Word>, N> readers;
0360       for (size_t i = 0; i < N; ++i) {
0361         const Bitmap& in_bitmap = bitmaps_arg[i];
0362         readers[i] =
0363             BitmapWordReader<Word>(in_bitmap.data_, in_bitmap.offset_, in_bitmap.length_);
0364       }
0365 
0366       std::array<BitmapWordWriter<Word>, M> writers;
0367       for (size_t i = 0; i < M; ++i) {
0368         const Bitmap& out_bitmap = out_bitmaps_arg->at(i);
0369         writers[i] = BitmapWordWriter<Word>(out_bitmap.mutable_data_, out_bitmap.offset_,
0370                                             out_bitmap.length_);
0371       }
0372 
0373       RunVisitWordsAndWriteLoop(bit_length, readers, writers, visitor);
0374     }
0375   }
0376 
0377   const uint8_t* data() const { return data_; }
0378   uint8_t* mutable_data() { return mutable_data_; }
0379 
0380   /// offset of first bit relative to buffer().data()
0381   int64_t offset() const { return offset_; }
0382 
0383   /// number of bits in this Bitmap
0384   int64_t length() const { return length_; }
0385 
0386   /// span of all bytes which contain any bit in this Bitmap
0387   util::span<const uint8_t> bytes() const {
0388     auto byte_offset = offset_ / 8;
0389     auto byte_count = bit_util::CeilDiv(offset_ + length_, 8) - byte_offset;
0390     return {data_ + byte_offset, static_cast<size_t>(byte_count)};
0391   }
0392 
0393  private:
0394   /// span of all Words which contain any bit in this Bitmap
0395   ///
0396   /// For example, given Word=uint16_t and a bitmap spanning bits [20, 36)
0397   /// words() would span bits [16, 48).
0398   ///
0399   /// 0       16      32     48     64
0400   /// |-------|-------|------|------| (buffer)
0401   ///           [       ]             (bitmap)
0402   ///         |-------|------|        (returned words)
0403   ///
0404   /// \warning The words may contain bytes which lie outside the buffer or are
0405   /// uninitialized.
0406   template <typename Word>
0407   util::span<const Word> words() const {
0408     auto bytes_addr = reinterpret_cast<intptr_t>(bytes().data());
0409     auto words_addr = bytes_addr - bytes_addr % sizeof(Word);
0410     auto word_byte_count =
0411         bit_util::RoundUpToPowerOf2(static_cast<int64_t>(bytes_addr + bytes().size()),
0412                                     static_cast<int64_t>(sizeof(Word))) -
0413         words_addr;
0414     return {reinterpret_cast<const Word*>(words_addr),
0415             static_cast<size_t>(word_byte_count / sizeof(Word))};
0416   }
0417 
0418   /// offset of first bit relative to words<Word>().data()
0419   template <typename Word>
0420   int64_t word_offset() const {
0421     return offset_ + 8 * (reinterpret_cast<intptr_t>(data_) -
0422                           reinterpret_cast<intptr_t>(words<Word>().data()));
0423   }
0424 
0425   /// load words from bitmaps bitwise
0426   template <size_t N, typename Word>
0427   static void SafeLoadWords(const Bitmap (&bitmaps)[N], int64_t offset,
0428                             int64_t out_length, bool set_trailing_bits,
0429                             std::array<Word, N>* out) {
0430     out->fill(0);
0431 
0432     int64_t out_offset = set_trailing_bits ? sizeof(Word) * 8 - out_length : 0;
0433 
0434     Bitmap slices[N], out_bitmaps[N];
0435     for (size_t i = 0; i < N; ++i) {
0436       slices[i] = bitmaps[i].Slice(offset, out_length);
0437       out_bitmaps[i] = Bitmap(&out->at(i), out_offset, out_length);
0438     }
0439 
0440     int64_t bit_i = 0;
0441     Bitmap::VisitBits(slices, [&](std::bitset<N> bits) {
0442       for (size_t i = 0; i < N; ++i) {
0443         out_bitmaps[i].SetBitTo(bit_i, bits[i]);
0444       }
0445       ++bit_i;
0446     });
0447   }
0448 
0449   /// assert bitmaps have identical length and return that length
0450   static int64_t BitLength(const Bitmap* bitmaps, size_t N);
0451 
0452   template <size_t N>
0453   static int64_t BitLength(const std::array<Bitmap, N>& bitmaps) {
0454     for (size_t i = 1; i < N; ++i) {
0455       assert(bitmaps[i].length() == bitmaps[0].length());
0456     }
0457     return bitmaps[0].length();
0458   }
0459 
0460   const uint8_t* data_ = NULLPTR;
0461   uint8_t* mutable_data_ = NULLPTR;
0462   int64_t offset_ = 0, length_ = 0;
0463 };
0464 
0465 }  // namespace internal
0466 }  // namespace arrow