/** * Copyright (c) 2021 OceanBase * OceanBase CE is licensed under Mulan PubL v2. * You can use this software according to the terms and conditions of the Mulan PubL v2. * You may obtain a copy of Mulan PubL v2 at: * http://license.coscl.org.cn/MulanPubL-2.0 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. * See the Mulan PubL v2 for more details. */ #ifndef OCEANBASE_ENGINE_OB_BIT_VECTOR_H_ #define OCEANBASE_ENGINE_OB_BIT_VECTOR_H_ #include #include #include "lib/ob_define.h" namespace oceanbase { namespace sql { // // Interpret data as bit vector, the caller's responsibility to make sure memory is enough // and never out of bound. // // Note: all operation is aligned to word bytes (8), see: // reset(), is_superset_of(), is_subset_of(). // // Why not ObBitSet? // We need a simple enough bit vector to skip rows in vectorized execution. // The bit vector is var-length and is preallocated before execution (may be allocated in CG), // the ObBitSet need constructor, too complex for us. (BTW: ObFixedRowSet is fix-length). struct ObBitVector { public: const static int64_t BYTES_PER_WORD = sizeof(uint64_t); const static int64_t DATA_ALIGN_SIZE = BYTES_PER_WORD; const static int64_t WORD_BITS = CHAR_BIT * BYTES_PER_WORD; ObBitVector() = default; ObBitVector(const ObBitVector&) = delete; ObBitVector& operator=(const ObBitVector&) = delete; ~ObBitVector() = default; inline static int64_t word_count(const int64_t size); inline static int64_t memory_size(const int64_t size); inline static int64_t byte_count(const int64_t size); void init(const int64_t size) { MEMSET(data_, 0, memory_size(size)); } void reset(const int64_t size) { init(size); } inline bool at(const int64_t idx) const; bool contain(const int64_t idx) const { return at(idx); } bool exist(const int64_t idx) const { return at(idx); } void deep_copy(const ObBitVector &src, const int64_t size) { MEMCPY(data_, src.data_, byte_count(size)); } inline void set(const int64_t idx); inline void unset(const int64_t idx); // at(i) |= v; inline void bit_or_assign(const int64_t idx, const bool v); // bit vector is superset of %other inline bool is_superset_of(const ObBitVector &other, const int64_t size) const; // bit vector is subset of %other inline bool is_subset_of(const ObBitVector &other, const int64_t size) const; // check all bits is zero after bit_op(left_bit, right_bit) // e.g.: // size: 8 // bit_op: l & (~r) // return: 0 == ((l.data_[0] & (~r.data_[0])) & 0xFF) template OB_INLINE static bool bit_op_zero(const ObBitVector &l, const ObBitVector &r, const int64_t size, T bit_op); // use param bit_op to calc l op r, assign the result to *this // e.g.: // bit_op : l & r // this->data_ = l.data_ & r.data_ template OB_INLINE void bit_calculate(const ObBitVector &l, const ObBitVector &r, const int64_t size, T bit_op); OB_INLINE void bit_not(const int64_t size); // accumulate bit count, CPU consuming operation, same as: // int64_t bit_cnt = 0; // for (int64_t i = 0; i < size; i++) { // bit_cnt += at(i); // } inline int64_t accumulate_bit_cnt(const int64_t size) const; inline void set_all(const int64_t size); inline bool is_all_true(const int64_t size) const; inline bool is_all_false(const int64_t size) const; // You should known how ObBitVector implemented, when reinterpret data. template T *reinterpret_data() { return reinterpret_cast(data_); } template const T *reinterpret_data() const { return reinterpret_cast(data_); } /** * access all valid fliped bit, flip a bit meaning change its value from 0 to 1 or from 1 to 0 * and it access all 0 bit */ template static OB_INLINE int flip_foreach(const ObBitVector &skip, int64_t size, OP op); /** * access all bit that it's 1 */ template static OB_INLINE int foreach(const ObBitVector &skip, int64_t size, OP op); private: OB_INLINE static int64_t popcount64(uint64_t v); template static OB_INLINE int inner_foreach(const ObBitVector &skip, int64_t size, OP op); public: uint64_t data_[0]; }; inline ObBitVector *to_bit_vector(void *mem) { return static_cast(mem); } inline const ObBitVector *to_bit_vector(const void *mem) { return static_cast(mem); } inline int64_t ObBitVector::word_count(const int64_t size) { return (size + WORD_BITS - 1) / WORD_BITS; } inline int64_t ObBitVector::byte_count(const int64_t size) { return (size + CHAR_BIT - 1) / CHAR_BIT; } inline int64_t ObBitVector::memory_size(const int64_t size) { return word_count(size) * BYTES_PER_WORD; } inline bool ObBitVector::at(const int64_t idx) const { OB_ASSERT(idx >= 0); // The modern compiler is smart enough to optimize this division to bit operations. // Do not bother yourself with that. return data_[idx / WORD_BITS] & (1LU << (idx % WORD_BITS)); } inline void ObBitVector::bit_or_assign(const int64_t idx, const bool v) { OB_ASSERT(idx >= 0); data_[idx / WORD_BITS] |= static_cast(v) << (idx % WORD_BITS); } inline void ObBitVector::set(const int64_t idx) { OB_ASSERT(idx >= 0); data_[idx / WORD_BITS] |= 1LU << (idx % WORD_BITS); } inline void ObBitVector::unset(const int64_t idx) { OB_ASSERT(idx >= 0); data_[idx / WORD_BITS] &= ~(1LU << (idx % WORD_BITS)); } // bit vector is superset of %other inline bool ObBitVector::is_superset_of(const ObBitVector &other, const int64_t size) const { const int64_t cnt = word_count(size); bool is = true; for (int64_t i = 0; i < cnt; i++) { if ((~data_[i]) & other.data_[i]) { is = false; break; } } return is; } // bit vector is subset of %other inline bool ObBitVector::is_subset_of(const ObBitVector &other, const int64_t size) const { return other.is_superset_of(*this, size); } template OB_INLINE bool ObBitVector::bit_op_zero(const ObBitVector &l, const ObBitVector &r, const int64_t size, T op) { bool passed = true; const int64_t cnt = size / WORD_BITS; for (int64_t i = 0; i < cnt; i++) { if (OB_UNLIKELY(0 != op(l.data_[i], r.data_[i]))) { passed = false; break; } } if (passed && 0 != size % WORD_BITS) { passed = 0 == (op(l.data_[cnt], r.data_[cnt]) & ((1LU << (size % WORD_BITS)) - 1)); } return passed; } template OB_INLINE void ObBitVector::bit_calculate(const ObBitVector &l, const ObBitVector &r, const int64_t size, T op) { const int64_t cnt = size / WORD_BITS; for (int64_t i = 0; i < cnt; ++i) { data_[i] = op(l.data_[i], r.data_[i]); } if (0 != size % WORD_BITS) { uint64_t tmp = data_[cnt]; data_[cnt] = ((op(l.data_[cnt], r.data_[cnt]) & ((1LU << (size % WORD_BITS)) - 1)) | (tmp & ~((1LU << (size % WORD_BITS)) - 1))); } } OB_INLINE void ObBitVector::bit_not(const int64_t size) { const int64_t cnt = size / WORD_BITS; for (int64_t i = 0; i < cnt; ++i) { data_[i] = ~(uint64_t)(data_[i]); } if (0 != size % WORD_BITS) { uint64_t tmp = data_[cnt]; data_[cnt] = (((~(uint64_t)(data_[cnt])) & ((1LU << (size % WORD_BITS)) - 1)) | (tmp & ~((1LU << (size % WORD_BITS)) - 1))); } } OB_INLINE int64_t ObBitVector::popcount64(uint64_t v) { int64_t cnt = 0; #if __POPCNT__ cnt = __builtin_popcountl(v); #else if (0 != v) { v = v - ((v >> 1) & 0x5555555555555555UL); v = (v & 0x3333333333333333UL) + ((v >> 2) & 0x3333333333333333UL); cnt = (((v + (v >> 4)) & 0xF0F0F0F0F0F0F0FUL) * 0x101010101010101UL) >> 56; } #endif return cnt; } inline int64_t ObBitVector::accumulate_bit_cnt(const int64_t size) const { int64_t bit_cnt = 0; const int64_t cnt = size / WORD_BITS; const int64_t remain = size % WORD_BITS; for (int64_t i = 0; i < cnt; i++) { uint64_t v = data_[i]; bit_cnt += popcount64(v); } if (remain > 0) { bit_cnt += popcount64(data_[cnt] & ((1LU << remain) - 1)); } return bit_cnt; } inline void ObBitVector::set_all(const int64_t size) { MEMSET(data_, 0xFF, BYTES_PER_WORD * (size / WORD_BITS)); if ( 0 != size % WORD_BITS) { data_[size / WORD_BITS] |= (1LU << (size % WORD_BITS)) - 1; } } inline bool ObBitVector::is_all_true(const int64_t size) const { bool is_all_true = true; const int64_t cnt = size / WORD_BITS; const int64_t remain = size % WORD_BITS; for (int64_t i = 0; is_all_true && i < cnt; i++) { uint64_t v = data_[i]; is_all_true = (uint64_t(-1) == v); } if (is_all_true && remain > 0) { uint64_t mask = ((1LU << remain) - 1); is_all_true = ((data_[cnt] & mask) == mask); } return is_all_true; } inline bool ObBitVector::is_all_false(const int64_t size) const { bool is_all_false = true; const int64_t cnt = size / WORD_BITS; const int64_t remain = size % WORD_BITS; for (int64_t i = 0; is_all_false && i < cnt; i++) { uint64_t v = data_[i]; is_all_false = (0 == v); } if (is_all_false && remain > 0) { uint64_t mask = ((1LU << remain) - 1); is_all_false = ((data_[cnt] & mask) == 0); } return is_all_false; } template OB_INLINE int ObBitVector::inner_foreach(const ObBitVector &skip, int64_t size, OP op) { int ret = OB_SUCCESS; int64_t tmp_step = 0; typedef uint16_t StepType; const int64_t step_size = sizeof(StepType) * CHAR_BIT; int64_t word_cnt = ObBitVector::word_count(size); int64_t step = 0; const int64_t remain = size % ObBitVector::WORD_BITS; for (int64_t i = 0; i < word_cnt && OB_SUCC(ret); ++i) { uint64_t s_word = (IS_FLIP ? ~skip.data_[i] : skip.data_[i]); // bool all_bits = (IS_FLIP ? skip.data_[i] == 0 : (~skip.data_[i]) == 0); if (i >= word_cnt - 1 && remain > 0) { // all_bits = ((IS_FLIP ? skip.data_[i] : ~skip.data_[i]) & ((1LU << remain) - 1)) == 0; s_word = s_word & ((1LU << remain) - 1); } if (s_word > 0) { uint64_t tmp_s_word = s_word; tmp_step = step; do { uint16_t step_val = tmp_s_word & 0xFFFF; if (0xFFFF == step_val) { // no skip // last batch ? int64_t mini_cnt = step_size; if (tmp_step + step_size > size) { mini_cnt = size - tmp_step; } for (int64_t j = 0; j < mini_cnt; j++) { int64_t k = j + tmp_step; ret = op(k); } } else if (step_val > 0) { do { int64_t start_bit_idx = __builtin_ctz(step_val); int64_t k = start_bit_idx + tmp_step; ret = op(k); step_val &= (step_val - 1); } while (step_val > 0 && OB_SUCC(ret)); // end for, for one step size } tmp_step += step_size; tmp_s_word >>= step_size; } while (tmp_s_word > 0 && OB_SUCC(ret)); // one word-uint64_t } step += ObBitVector::WORD_BITS; } // end for return ret; } template OB_INLINE int ObBitVector::flip_foreach(const ObBitVector &skip, int64_t size, OP op) { return ObBitVector::inner_foreach(skip, size, op); } template OB_INLINE int ObBitVector::foreach(const ObBitVector &skip, int64_t size, OP op) { return ObBitVector::inner_foreach(skip, size, op); } } // end namespace sql } // end namespace oceanbase #endif // OCEANBASE_ENGINE_OB_BIT_VECTOR_H_