689 lines
29 KiB
C
689 lines
29 KiB
C
|
// This file is part of Blend2D project <https://blend2d.com>
|
||
|
//
|
||
|
// See blend2d.h or LICENSE.md for license and copyright information
|
||
|
// SPDX-License-Identifier: Zlib
|
||
|
|
||
|
#ifndef BLEND2D_BITSET_H
|
||
|
#define BLEND2D_BITSET_H
|
||
|
|
||
|
#include "object.h"
|
||
|
|
||
|
//! \addtogroup blend2d_api_globals
|
||
|
//! \{
|
||
|
|
||
|
//! \name BLBitSet - Constants
|
||
|
//! \{
|
||
|
|
||
|
BL_DEFINE_ENUM(BLBitSetConstants) {
|
||
|
//! Invalid bit-index.
|
||
|
//!
|
||
|
//! This is the only index that cannot be stored in `BLBitSet`.
|
||
|
BL_BIT_SET_INVALID_INDEX = 0xFFFFFFFFu,
|
||
|
|
||
|
//! Range mask used by `BLBitsetSegment::start` value - if set the segment is a range of all ones.
|
||
|
BL_BIT_SET_RANGE_MASK = 0x80000000u,
|
||
|
|
||
|
//! Number of words in a BLBitSetSegment.
|
||
|
BL_BIT_SET_SEGMENT_WORD_COUNT = 4u
|
||
|
};
|
||
|
|
||
|
//! \}
|
||
|
|
||
|
//! \name BLBitSet - Structs
|
||
|
//! \{
|
||
|
|
||
|
//! BitSet segment.
|
||
|
//!
|
||
|
//! Segment provides either a dense set of bits starting at `start` or a range of bits all set to one. The start of
|
||
|
//! the segment is always aligned to segment size, which can be calculated as `32 * BL_BIT_SET_SEGMENT_WORD_COUNT`.
|
||
|
//! Even ranges are aligned to this value, thus up to 3 segments are used to describe a range that doesn't start/end
|
||
|
//! at the segment boundary.
|
||
|
//!
|
||
|
//! When the segment describes dense bits its size is always fixed and represents `32 * BL_BIT_SET_SEGMENT_WORD_COUNT`
|
||
|
//! bits, which is currently 128 bits. However, when the segment describes all ones, the first value in data `data[0]`
|
||
|
//! describes the last bit of the range, which means that an arbitrary range can be encoded within a single segment.
|
||
|
struct BLBitSetSegment {
|
||
|
uint32_t _startWord;
|
||
|
uint32_t _data[BL_BIT_SET_SEGMENT_WORD_COUNT];
|
||
|
|
||
|
#ifdef __cplusplus
|
||
|
BL_INLINE_NODEBUG bool allOnes() const noexcept { return (_startWord & BL_BIT_SET_RANGE_MASK) != 0; }
|
||
|
BL_INLINE_NODEBUG void clearData() noexcept { memset(_data, 0, sizeof(_data)); }
|
||
|
BL_INLINE_NODEBUG void fillData() noexcept { memset(_data, 0xFF, sizeof(_data)); }
|
||
|
|
||
|
BL_INLINE_NODEBUG uint32_t* data() noexcept { return _data; }
|
||
|
BL_INLINE_NODEBUG const uint32_t* data() const noexcept { return _data; }
|
||
|
BL_INLINE_NODEBUG uint32_t wordAt(size_t index) const noexcept { return _data[index]; }
|
||
|
|
||
|
BL_INLINE_NODEBUG uint32_t _rangeStartWord() const noexcept { return _startWord & ~BL_BIT_SET_RANGE_MASK; }
|
||
|
BL_INLINE_NODEBUG uint32_t _rangeEndWord() const noexcept { return _data[0]; }
|
||
|
|
||
|
BL_INLINE_NODEBUG uint32_t _denseStartWord() const noexcept { return _startWord; }
|
||
|
BL_INLINE_NODEBUG uint32_t _denseEndWord() const noexcept { return _startWord + BL_BIT_SET_SEGMENT_WORD_COUNT; }
|
||
|
|
||
|
BL_INLINE_NODEBUG void _setRangeStartWord(uint32_t index) noexcept { _startWord = index; }
|
||
|
BL_INLINE_NODEBUG void _setRangeEndWord(uint32_t index) noexcept { _data[0] = index; }
|
||
|
|
||
|
BL_INLINE_NODEBUG uint32_t startWord() const noexcept { return _startWord & ~BL_BIT_SET_RANGE_MASK; }
|
||
|
BL_INLINE_NODEBUG uint32_t startSegmentId() const noexcept { return startWord() / BL_BIT_SET_SEGMENT_WORD_COUNT; }
|
||
|
BL_INLINE_NODEBUG uint32_t startBit() const noexcept { return _startWord * 32u; }
|
||
|
|
||
|
BL_INLINE uint32_t endWord() const noexcept {
|
||
|
uint32_t rangeEnd = _rangeEndWord();
|
||
|
uint32_t denseEnd = _denseEndWord();
|
||
|
return allOnes() ? rangeEnd : denseEnd;
|
||
|
}
|
||
|
|
||
|
BL_INLINE_NODEBUG uint32_t endSegmentId() const noexcept { return endWord() / BL_BIT_SET_SEGMENT_WORD_COUNT; }
|
||
|
BL_INLINE_NODEBUG uint32_t lastBit() const noexcept { return endWord() * 32u - 1u; }
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
//! BitSet data view.
|
||
|
struct BLBitSetData {
|
||
|
const BLBitSetSegment* segmentData;
|
||
|
uint32_t segmentCount;
|
||
|
BLBitSetSegment ssoSegments[3];
|
||
|
|
||
|
#ifdef __cplusplus
|
||
|
BL_INLINE_NODEBUG bool empty() const noexcept {
|
||
|
return segmentCount == 0;
|
||
|
}
|
||
|
|
||
|
BL_INLINE void reset() noexcept {
|
||
|
segmentData = nullptr;
|
||
|
segmentCount = 0;
|
||
|
}
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
//! \}
|
||
|
|
||
|
//! \name BLBitSet - C API
|
||
|
//! \{
|
||
|
|
||
|
BL_BEGIN_C_DECLS
|
||
|
|
||
|
BL_API BLResult BL_CDECL blBitSetInit(BLBitSetCore* self) BL_NOEXCEPT_C;
|
||
|
BL_API BLResult BL_CDECL blBitSetInitMove(BLBitSetCore* self, BLBitSetCore* other) BL_NOEXCEPT_C;
|
||
|
BL_API BLResult BL_CDECL blBitSetInitWeak(BLBitSetCore* self, const BLBitSetCore* other) BL_NOEXCEPT_C;
|
||
|
BL_API BLResult BL_CDECL blBitSetInitRange(BLBitSetCore* self, uint32_t startBit, uint32_t endBit) BL_NOEXCEPT_C;
|
||
|
BL_API BLResult BL_CDECL blBitSetDestroy(BLBitSetCore* self) BL_NOEXCEPT_C;
|
||
|
BL_API BLResult BL_CDECL blBitSetReset(BLBitSetCore* self) BL_NOEXCEPT_C;
|
||
|
BL_API BLResult BL_CDECL blBitSetAssignMove(BLBitSetCore* self, BLBitSetCore* other) BL_NOEXCEPT_C;
|
||
|
BL_API BLResult BL_CDECL blBitSetAssignWeak(BLBitSetCore* self, const BLBitSetCore* other) BL_NOEXCEPT_C;
|
||
|
BL_API BLResult BL_CDECL blBitSetAssignDeep(BLBitSetCore* self, const BLBitSetCore* other) BL_NOEXCEPT_C;
|
||
|
BL_API BLResult BL_CDECL blBitSetAssignRange(BLBitSetCore* self, uint32_t startBit, uint32_t endBit) BL_NOEXCEPT_C;
|
||
|
BL_API BLResult BL_CDECL blBitSetAssignWords(BLBitSetCore* self, uint32_t startWord, const uint32_t* wordData, uint32_t wordCount) BL_NOEXCEPT_C;
|
||
|
BL_API bool BL_CDECL blBitSetIsEmpty(const BLBitSetCore* self) BL_NOEXCEPT_C;
|
||
|
BL_API BLResult BL_CDECL blBitSetGetData(const BLBitSetCore* self, BLBitSetData* out) BL_NOEXCEPT_C;
|
||
|
BL_API uint32_t BL_CDECL blBitSetGetSegmentCount(const BLBitSetCore* self) BL_NOEXCEPT_C BL_PURE;
|
||
|
BL_API uint32_t BL_CDECL blBitSetGetSegmentCapacity(const BLBitSetCore* self) BL_NOEXCEPT_C BL_PURE;
|
||
|
BL_API uint32_t BL_CDECL blBitSetGetCardinality(const BLBitSetCore* self) BL_NOEXCEPT_C;
|
||
|
BL_API uint32_t BL_CDECL blBitSetGetCardinalityInRange(const BLBitSetCore* self, uint32_t startBit, uint32_t endBit) BL_NOEXCEPT_C;
|
||
|
BL_API bool BL_CDECL blBitSetHasBit(const BLBitSetCore* self, uint32_t bitIndex) BL_NOEXCEPT_C;
|
||
|
BL_API bool BL_CDECL blBitSetHasBitsInRange(const BLBitSetCore* self, uint32_t startBit, uint32_t endBit) BL_NOEXCEPT_C;
|
||
|
BL_API bool BL_CDECL blBitSetSubsumes(const BLBitSetCore* a, const BLBitSetCore* b) BL_NOEXCEPT_C;
|
||
|
BL_API bool BL_CDECL blBitSetIntersects(const BLBitSetCore* a, const BLBitSetCore* b) BL_NOEXCEPT_C;
|
||
|
BL_API bool BL_CDECL blBitSetGetRange(const BLBitSetCore* self, uint32_t* startOut, uint32_t* endOut) BL_NOEXCEPT_C;
|
||
|
BL_API bool BL_CDECL blBitSetEquals(const BLBitSetCore* a, const BLBitSetCore* b) BL_NOEXCEPT_C;
|
||
|
BL_API int BL_CDECL blBitSetCompare(const BLBitSetCore* a, const BLBitSetCore* b) BL_NOEXCEPT_C;
|
||
|
BL_API BLResult BL_CDECL blBitSetClear(BLBitSetCore* self) BL_NOEXCEPT_C;
|
||
|
BL_API BLResult BL_CDECL blBitSetShrink(BLBitSetCore* self) BL_NOEXCEPT_C;
|
||
|
BL_API BLResult BL_CDECL blBitSetOptimize(BLBitSetCore* self) BL_NOEXCEPT_C;
|
||
|
BL_API BLResult BL_CDECL blBitSetChop(BLBitSetCore* self, uint32_t startBit, uint32_t endBit) BL_NOEXCEPT_C;
|
||
|
BL_API BLResult BL_CDECL blBitSetAddBit(BLBitSetCore* self, uint32_t bitIndex) BL_NOEXCEPT_C;
|
||
|
BL_API BLResult BL_CDECL blBitSetAddRange(BLBitSetCore* self, uint32_t rangeStartBit, uint32_t rangeEndBit) BL_NOEXCEPT_C;
|
||
|
BL_API BLResult BL_CDECL blBitSetAddWords(BLBitSetCore* self, uint32_t startWord, const uint32_t* wordData, uint32_t wordCount) BL_NOEXCEPT_C;
|
||
|
BL_API BLResult BL_CDECL blBitSetClearBit(BLBitSetCore* self, uint32_t bitIndex) BL_NOEXCEPT_C;
|
||
|
BL_API BLResult BL_CDECL blBitSetClearRange(BLBitSetCore* self, uint32_t rangeStartBit, uint32_t rangeEndBit) BL_NOEXCEPT_C;
|
||
|
|
||
|
// TODO: Future API (BitSet).
|
||
|
/*
|
||
|
BL_API BLResult BL_CDECL blBitSetCombine(BLBitSetCore* dst, const BLBitSetCore* a, const BLBitSetCore* b, BLBooleanOp booleanOp) BL_NOEXCEPT_C;
|
||
|
*/
|
||
|
|
||
|
BL_API BLResult BL_CDECL blBitSetBuilderCommit(BLBitSetCore* self, BLBitSetBuilderCore* builder, uint32_t newAreaIndex) BL_NOEXCEPT_C;
|
||
|
BL_API BLResult BL_CDECL blBitSetBuilderAddRange(BLBitSetCore* self, BLBitSetBuilderCore* builder, uint32_t startBit, uint32_t endBit) BL_NOEXCEPT_C;
|
||
|
|
||
|
BL_END_C_DECLS
|
||
|
|
||
|
//! BitSet container [C API].
|
||
|
struct BLBitSetCore BL_CLASS_INHERITS(BLObjectCore) {
|
||
|
BL_DEFINE_OBJECT_DETAIL
|
||
|
BL_DEFINE_OBJECT_DCAST(BLBitSet)
|
||
|
};
|
||
|
|
||
|
//! BitSet builder [C API].
|
||
|
struct BLBitSetBuilderCore {
|
||
|
//! Shift to get `_areaIndex` from bit index, equals to `log2(kBitCount)`.
|
||
|
uint32_t _areaShift;
|
||
|
//! Area index - index from 0...N where each index represents `kBitCount` bits.
|
||
|
uint32_t _areaIndex;
|
||
|
|
||
|
/*
|
||
|
//! Area word data.
|
||
|
uint32_t areaWords[1 << (areaShift - 5)];
|
||
|
*/
|
||
|
|
||
|
#ifdef __cplusplus
|
||
|
enum : uint32_t {
|
||
|
kInvalidAreaIndex = 0xFFFFFFFFu
|
||
|
};
|
||
|
|
||
|
BL_INLINE_NODEBUG uint32_t* areaWords() noexcept { return reinterpret_cast<uint32_t*>(this + 1); }
|
||
|
BL_INLINE_NODEBUG const uint32_t* areaWords() const noexcept { return reinterpret_cast<const uint32_t*>(this + 1); }
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
//! \}
|
||
|
|
||
|
//! \cond INTERNAL
|
||
|
//! \name BLBitSet - Internals
|
||
|
//! \{
|
||
|
|
||
|
//! BitSet container [Impl].
|
||
|
struct BLBitSetImpl BL_CLASS_INHERITS(BLObjectImpl) {
|
||
|
//! Count of used segments in `segmentData`.
|
||
|
uint32_t segmentCount;
|
||
|
//! Count of allocated segments in `segmentData`.
|
||
|
uint32_t segmentCapacity;
|
||
|
|
||
|
#ifdef __cplusplus
|
||
|
BL_INLINE_NODEBUG BLBitSetSegment* segmentData() const noexcept { return (BLBitSetSegment*)(this + 1); }
|
||
|
BL_INLINE_NODEBUG BLBitSetSegment* segmentDataEnd() const noexcept { return segmentData() + segmentCount; }
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
//! \}
|
||
|
//! \endcond
|
||
|
|
||
|
//! \name BLBitSet - C++ API
|
||
|
//! \{
|
||
|
#ifdef __cplusplus
|
||
|
|
||
|
//! BitSet container [C++ API].
|
||
|
//!
|
||
|
//! BitSet container implements sparse BitSet that consists of segments, where each segment represents either dense
|
||
|
//! range of bits or a range of bits that are all set to one. In addition, the BitSet provides also a SSO mode, in
|
||
|
//! which it's possible to store up to 64 dense bits (2 consecutive BitWords) in the whole addressable range or a
|
||
|
//! range of ones. SSO mode optimizes use cases, in which very small BitSets are needed.
|
||
|
//!
|
||
|
//! The BitSet itself has been optimized for Blend2D use cases, which are the following:
|
||
|
//!
|
||
|
//! 1. Representing character coverage of fonts and unicode text. This use-case requires sparseness and ranges as
|
||
|
//! some fonts, especially those designed for CJK use, provide thousands of glyphs that have pretty high code
|
||
|
//! points - using BLBitArray would be very wasteful in this particular case.
|
||
|
class BLBitSet final : public BLBitSetCore {
|
||
|
public:
|
||
|
//! \cond INTERNAL
|
||
|
//! \name Internals
|
||
|
//! \{
|
||
|
|
||
|
enum : uint32_t {
|
||
|
//! Number of words that can be used by SSO dense representation (2 words => 64 bits).
|
||
|
kSSOWordCount = 2,
|
||
|
kSSODenseSignature = BLObjectInfo::packTypeWithMarker(BL_OBJECT_TYPE_BIT_SET),
|
||
|
kSSOEmptySignature = BLObjectInfo::packTypeWithMarker(BL_OBJECT_TYPE_BIT_SET) | BL_OBJECT_INFO_R_FLAG
|
||
|
};
|
||
|
|
||
|
BL_INLINE_NODEBUG BLBitSetImpl* _impl() const noexcept { return static_cast<BLBitSetImpl*>(_d.impl); }
|
||
|
|
||
|
BL_INLINE_NODEBUG void _initRangeInternal(uint32_t startBit = 0u, uint32_t endBit = 0u) noexcept {
|
||
|
_d.initStatic(BLObjectInfo{kSSOEmptySignature});
|
||
|
_d.u32_data[0] = startBit;
|
||
|
_d.u32_data[1] = endBit;
|
||
|
}
|
||
|
|
||
|
//! \}
|
||
|
//! \endcond
|
||
|
|
||
|
//! \name Construction & Destruction
|
||
|
//! \{
|
||
|
|
||
|
BL_INLINE BLBitSet() noexcept { _initRangeInternal(); }
|
||
|
|
||
|
BL_INLINE BLBitSet(BLBitSet&& other) noexcept {
|
||
|
_d = other._d;
|
||
|
other._initRangeInternal();
|
||
|
}
|
||
|
|
||
|
BL_INLINE BLBitSet(const BLBitSet& other) noexcept { blBitSetInitWeak(this, &other); }
|
||
|
|
||
|
BL_INLINE BLBitSet(uint32_t startBit, uint32_t endBit) noexcept {
|
||
|
uint32_t mask = uint32_t(-int32_t(startBit < endBit));
|
||
|
_initRangeInternal(startBit & mask, endBit & mask);
|
||
|
}
|
||
|
|
||
|
//! Destroys the BitSet.
|
||
|
BL_INLINE ~BLBitSet() noexcept {
|
||
|
if (BLInternal::objectNeedsCleanup(_d.info.bits))
|
||
|
blBitSetDestroy(this);
|
||
|
}
|
||
|
|
||
|
//! \}
|
||
|
|
||
|
//! \name Overloaded Operators
|
||
|
//! \{
|
||
|
|
||
|
//! Tests whether the BitSet has a content.
|
||
|
//!
|
||
|
//! \note This is essentially the opposite of `empty()`.
|
||
|
BL_INLINE explicit operator bool() const noexcept { return !empty(); }
|
||
|
|
||
|
//! Move assignment.
|
||
|
//!
|
||
|
//! \note The `other` BitSet is reset by move assignment, so its state after the move operation is the same as
|
||
|
//! a default constructed BitSet.
|
||
|
BL_INLINE BLBitSet& operator=(BLBitSet&& other) noexcept { blBitSetAssignMove(this, &other); return *this; }
|
||
|
//! Copy assignment, performs weak copy of the data held by the `other` BitSet.
|
||
|
BL_INLINE BLBitSet& operator=(const BLBitSet& other) noexcept { blBitSetAssignWeak(this, &other); return *this; }
|
||
|
|
||
|
BL_INLINE bool operator==(const BLBitSet& other) const noexcept { return equals(other); }
|
||
|
BL_INLINE bool operator!=(const BLBitSet& other) const noexcept { return !equals(other); }
|
||
|
BL_INLINE bool operator<(const BLBitSet& other) const noexcept { return compare(other) < 0; }
|
||
|
BL_INLINE bool operator<=(const BLBitSet& other) const noexcept { return compare(other) <= 0; }
|
||
|
BL_INLINE bool operator>(const BLBitSet& other) const noexcept { return compare(other) > 0; }
|
||
|
BL_INLINE bool operator>=(const BLBitSet& other) const noexcept { return compare(other) >= 0; }
|
||
|
|
||
|
//! \}
|
||
|
|
||
|
//! \name Common Functionality
|
||
|
//! \{
|
||
|
|
||
|
//! Clears the content of the BitSet and releases its data.
|
||
|
//!
|
||
|
//! After reset the BitSet content matches a default constructed instance.
|
||
|
BL_INLINE BLResult reset() noexcept { return blBitSetReset(this); }
|
||
|
|
||
|
//! Swaps the content of this string with the `other` string.
|
||
|
BL_INLINE void swap(BLBitSetCore& other) noexcept { _d.swap(other._d); }
|
||
|
|
||
|
//! \name Accessors
|
||
|
//! \{
|
||
|
|
||
|
//! Tests whether the BitSet is empty (has no content).
|
||
|
//!
|
||
|
//! Returns `true` if the BitSet's size is zero.
|
||
|
BL_INLINE bool empty() const noexcept { return blBitSetIsEmpty(this); }
|
||
|
|
||
|
//! Returns the number of segments this BitSet occupies.
|
||
|
//!
|
||
|
//! \note If the BitSet is in SSO mode then the returned value is the number of segments the BitSet would occupy
|
||
|
//! when the BitSet was converted to dynamic.
|
||
|
BL_INLINE uint32_t segmentCount() const noexcept { return blBitSetGetSegmentCount(this); }
|
||
|
|
||
|
//! Returns the number of segments this BitSet has allocated.
|
||
|
//!
|
||
|
//! \note If the BitSet is in SSO mode the returned value is always zero.
|
||
|
BL_INLINE uint32_t segmentCapacity() const noexcept { return _d.sso() ? 0 : _impl()->segmentCapacity; }
|
||
|
|
||
|
//! Returns the range of the BitSet as `[startOut, endOut)`.
|
||
|
//!
|
||
|
//! Returns true if the query was successful, false if the BitSet is empty.
|
||
|
BL_INLINE bool getRange(uint32_t* startOut, uint32_t* endOut) const noexcept { return blBitSetGetRange(this, startOut, endOut); }
|
||
|
|
||
|
//! Returns the number of bits set in the BitSet.
|
||
|
BL_INLINE uint32_t cardinality() const noexcept { return blBitSetGetCardinality(this); }
|
||
|
|
||
|
//! Returns the number of bits set in the given `[startBit, endBit)` range.
|
||
|
BL_INLINE uint32_t cardinalityInRange(uint32_t startBit, uint32_t endBit) const noexcept { return blBitSetGetCardinalityInRange(this, startBit, endBit); }
|
||
|
|
||
|
//! Stores a normalized BitSet data represented as segments into `out`.
|
||
|
//!
|
||
|
//! If the BitSet is in SSO mode, it will be converter to temporary segments provided by `BLBitSetData::ssoSegments`,
|
||
|
//! if the BitSet is in dynamic mode (already contains segments) then only a pointer to the data will be stored into
|
||
|
//! `out`.
|
||
|
//!
|
||
|
//! \remarks The data written into `out` can reference the data in the BitSet, thus the BitSet cannot be manipulated
|
||
|
//! during the use of the data. This function is ideal for inspecting the content of the BitSet in a unique way and
|
||
|
//! for implementing iterators that don't have to be aware of how SSO data is represented and used.
|
||
|
BL_INLINE BLResult getData(BLBitSetData* out) const noexcept { return blBitSetGetData(this, out); }
|
||
|
|
||
|
//! \}
|
||
|
|
||
|
//! \name Test Operations
|
||
|
//! \{
|
||
|
|
||
|
//! Returns a bit-value at the given `bitIndex`.
|
||
|
BL_INLINE bool hasBit(uint32_t bitIndex) const noexcept { return blBitSetHasBit(this, bitIndex); }
|
||
|
//! Returns whether the bit-set has at least on bit in the given range `[start:end)`.
|
||
|
BL_INLINE bool hasBitsInRange(uint32_t startBit, uint32_t endBit) const noexcept { return blBitSetHasBitsInRange(this, startBit, endBit); }
|
||
|
|
||
|
//! Returns whether this BitSet subsumes `other`.
|
||
|
BL_INLINE bool subsumes(const BLBitSetCore& other) const noexcept { return blBitSetSubsumes(this, &other); }
|
||
|
//! Returns whether this BitSet intersects with `other`.
|
||
|
BL_INLINE bool intersects(const BLBitSetCore& other) const noexcept { return blBitSetIntersects(this, &other); }
|
||
|
|
||
|
//! \}
|
||
|
|
||
|
//! \name Equality & Comparison
|
||
|
//! \{
|
||
|
|
||
|
//! Returns whether this BitSet and `other` are bitwise equal.
|
||
|
BL_INLINE bool equals(const BLBitSetCore& other) const noexcept { return blBitSetEquals(this, &other); }
|
||
|
//! Compares this BitSet with `other` and returns either `-1`, `0`, or `1`.
|
||
|
BL_INLINE int compare(const BLBitSetCore& other) const noexcept { return blBitSetCompare(this, &other); }
|
||
|
|
||
|
//! \}
|
||
|
|
||
|
//! \name Content Manipulation
|
||
|
//! \{
|
||
|
|
||
|
//! Move assignment, the same as `operator=`, but returns a `BLResult` instead of `this`.
|
||
|
BL_INLINE BLResult assign(BLBitSetCore&& other) noexcept { return blBitSetAssignMove(this, &other); }
|
||
|
|
||
|
//! Copy assignment, the same as `operator=`, but returns a `BLResult` instead of `this`.
|
||
|
BL_INLINE BLResult assign(const BLBitSetCore& other) noexcept { return blBitSetAssignWeak(this, &other); }
|
||
|
|
||
|
//! Copy assignment, but creates a deep copy of the `other` BitSet instead of weak copy.
|
||
|
BL_INLINE BLResult assignDeep(const BLBitSetCore& other) noexcept { return blBitSetAssignDeep(this, &other); }
|
||
|
|
||
|
//! Replaces the content of the BitSet by the given range.
|
||
|
BL_INLINE BLResult assignRange(uint32_t startBit, uint32_t endBit) noexcept { return blBitSetAssignRange(this, startBit, endBit); }
|
||
|
|
||
|
//! Replaces the content of the BitSet by bits specified by `wordData` of size `wordCount` [the size is in uint32_t units].
|
||
|
BL_INLINE BLResult assignWords(uint32_t startWord, const uint32_t* wordData, uint32_t wordCount) noexcept { return blBitSetAssignWords(this, startWord, wordData, wordCount); }
|
||
|
|
||
|
//! Clears the content of the BitSet without releasing its dynamically allocated data, if possible.
|
||
|
BL_INLINE BLResult clear() noexcept { return blBitSetClear(this); }
|
||
|
|
||
|
//! Shrinks the capacity of the BitSet to match the actual content.
|
||
|
BL_INLINE BLResult shrink() noexcept { return blBitSetShrink(this); }
|
||
|
|
||
|
//! Optimizes the BitSet by clearing unused pages and by merging continuous pages, without reallocating the BitSet.
|
||
|
//! This functions should always return `BL_SUCCESS`.
|
||
|
BL_INLINE BLResult optimize() noexcept { return blBitSetOptimize(this); }
|
||
|
|
||
|
//! Bounds the BitSet to the given interval `[start:end)`.
|
||
|
BL_INLINE BLResult chop(uint32_t startBit, uint32_t endBit) noexcept { return blBitSetChop(this, startBit, endBit); }
|
||
|
//! Truncates the BitSet so it's maximum bit set is less than `n`.
|
||
|
BL_INLINE BLResult truncate(uint32_t n) noexcept { return blBitSetChop(this, 0, n); }
|
||
|
|
||
|
//! Adds a bit to the BitSet at the given `index`.
|
||
|
BL_INLINE BLResult addBit(uint32_t bitIndex) noexcept { return blBitSetAddBit(this, bitIndex); }
|
||
|
//! Adds a range of bits `[rangeStartBit:rangeEndBit)` to the BitSet.
|
||
|
BL_INLINE BLResult addRange(uint32_t rangeStartBit, uint32_t rangeEndBit) noexcept { return blBitSetAddRange(this, rangeStartBit, rangeEndBit); }
|
||
|
//! Adds a dense data to the BitSet starting a bit index `start`.
|
||
|
BL_INLINE BLResult addWords(uint32_t startWord, const uint32_t* wordData, uint32_t wordCount) noexcept { return blBitSetAddWords(this, startWord, wordData, wordCount); }
|
||
|
|
||
|
//! Clears a bit in the BitSet at the given `index`.
|
||
|
BL_INLINE BLResult clearBit(uint32_t bitIndex) noexcept { return blBitSetClearBit(this, bitIndex); }
|
||
|
//! Clears a range of bits `[rangeStartBit:rangeEndBit)` in the BitSet.
|
||
|
BL_INLINE BLResult clearRange(uint32_t rangeStartBit, uint32_t rangeEndBit) noexcept { return blBitSetClearRange(this, rangeStartBit, rangeEndBit); }
|
||
|
|
||
|
/*
|
||
|
// TODO: Future API (BitSet).
|
||
|
|
||
|
BL_INLINE BLResult and_(const BLBitSetCore& other) noexcept { return blBitSetCombine(this, this, &other, BL_BOOLEAN_OP_AND); }
|
||
|
BL_INLINE BLResult or_(const BLBitSetCore& other) noexcept { return blBitSetCombine(this, this, &other, BL_BOOLEAN_OP_OR); }
|
||
|
BL_INLINE BLResult xor_(const BLBitSetCore& other) noexcept { return blBitSetCombine(this, this, &other, BL_BOOLEAN_OP_XOR); }
|
||
|
BL_INLINE BLResult andNot(const BLBitSetCore& other) noexcept { return blBitSetCombine(this, this, &other, BL_BOOLEAN_OP_AND_NOT); }
|
||
|
BL_INLINE BLResult notAnd(const BLBitSetCore& other) noexcept { return blBitSetCombine(this, this, &other, BL_BOOLEAN_OP_NOT_AND); }
|
||
|
BL_INLINE BLResult combine(const BLBitSetCore& other, BLBooleanOp booleanOp) noexcept { return blBitSetCombine(this, this, &other, booleanOp); }
|
||
|
|
||
|
static BL_INLINE BLResult and_(BLBitSetCore& dst, const BLBitSetCore& a, const BLBitSetCore& b) noexcept { return blBitSetCombine(&dst, &a, &b, BL_BOOLEAN_OP_AND); }
|
||
|
static BL_INLINE BLResult or_(BLBitSetCore& dst, const BLBitSetCore& a, const BLBitSetCore& b) noexcept { return blBitSetCombine(&dst, &a, &b, BL_BOOLEAN_OP_OR); }
|
||
|
static BL_INLINE BLResult xor_(BLBitSetCore& dst, const BLBitSetCore& a, const BLBitSetCore& b) noexcept { return blBitSetCombine(&dst, &a, &b, BL_BOOLEAN_OP_XOR); }
|
||
|
static BL_INLINE BLResult andNot(BLBitSetCore& dst, const BLBitSetCore& a, const BLBitSetCore& b) noexcept { return blBitSetCombine(&dst, &a, &b, BL_BOOLEAN_OP_AND_NOT); }
|
||
|
static BL_INLINE BLResult notAnd(BLBitSetCore& dst, const BLBitSetCore& a, const BLBitSetCore& b) noexcept { return blBitSetCombine(&dst, &a, &b, BL_BOOLEAN_OP_NOT_AND); }
|
||
|
static BL_INLINE BLResult combine(BLBitSetCore& dst, const BLBitSetCore& a, const BLBitSetCore& b, BLBooleanOp booleanOp) noexcept { return blBitSetCombine(&dst, &a, &b, booleanOp); }
|
||
|
*/
|
||
|
|
||
|
//! \}
|
||
|
};
|
||
|
|
||
|
//! BitSet builder [C++ API].
|
||
|
//!
|
||
|
//! BitSet builder is a low-level utility class that can be used to efficiently build a BitSet in C++. It maintains
|
||
|
//! a configurable buffer (called area) where intermediate bits are set, which are then committed to BitSet when
|
||
|
//! an added bit/range is outside of the area or when user is done with BitSet building. The commit uses \ref
|
||
|
//! blBitSetBuilderCommit() function, which was specifically designed for `BLBitSetBuilderT<BitCount>` in addition
|
||
|
//! to the `BLBitSetBuilder` alias.
|
||
|
//!
|
||
|
//! \note The destructor doesn't do anything. If there are still bits to be committed, they will be lost.
|
||
|
template<uint32_t BitCount>
|
||
|
class BLBitSetBuilderT final : public BLBitSetBuilderCore {
|
||
|
public:
|
||
|
static_assert(BitCount >= 128, "BitCount must be at least 128");
|
||
|
static_assert((BitCount & (BitCount - 1)) == 0, "BitCount must be a power of 2");
|
||
|
|
||
|
//! \name Constants
|
||
|
//! \{
|
||
|
|
||
|
enum : uint32_t {
|
||
|
kAreaBitCount = BitCount,
|
||
|
kAreaWordCount = kAreaBitCount / uint32_t(sizeof(uint32_t) * 8u),
|
||
|
kAreaShift = BLInternal::ConstCTZ<kAreaBitCount>::kValue
|
||
|
};
|
||
|
|
||
|
//! \}
|
||
|
|
||
|
//! \name Members
|
||
|
//! \{
|
||
|
|
||
|
//! Area words data.
|
||
|
uint32_t _areaWords[kAreaWordCount];
|
||
|
|
||
|
//! BitSet we are building.
|
||
|
//!
|
||
|
//! \note This member is not part of C API. C API requires both BLBitSetCore and BLBitSetBuilderCore to be passed.
|
||
|
BLBitSetCore* _bitSet;
|
||
|
|
||
|
//! \}
|
||
|
|
||
|
//! \name Construction & Destruction
|
||
|
//! \{
|
||
|
|
||
|
//! Constructs a new BitSet builder having no BitSet assigned.
|
||
|
BL_INLINE BLBitSetBuilderT() noexcept {
|
||
|
_bitSet = nullptr;
|
||
|
_areaShift = kAreaShift;
|
||
|
_areaIndex = kInvalidAreaIndex;
|
||
|
}
|
||
|
|
||
|
//! Constructs a new BitSet builder having the given `bitSet` assigned.
|
||
|
//!
|
||
|
//! \note The builder only stores a pointer to the `bitSet` - the user must guarantee to not destroy the BitSet
|
||
|
//! before the builder is destroyed or reset.
|
||
|
BL_INLINE explicit BLBitSetBuilderT(BLBitSetCore* bitSet) noexcept {
|
||
|
_bitSet = bitSet;
|
||
|
_areaShift = kAreaShift;
|
||
|
_areaIndex = kInvalidAreaIndex;
|
||
|
}
|
||
|
|
||
|
BL_INLINE BLBitSetBuilderT(const BLBitSetBuilderT&) = delete;
|
||
|
BL_INLINE BLBitSetBuilderT& operator=(const BLBitSetBuilderT&) = delete;
|
||
|
|
||
|
//! \}
|
||
|
|
||
|
//! \name Accessors
|
||
|
//! \{
|
||
|
|
||
|
//! Returns whether the BitSet builder is valid, which means that is has an associated `BLBitSet` instance.
|
||
|
BL_INLINE bool isValid() const noexcept { return _bitSet != nullptr; }
|
||
|
//! Returns an associated `BLBitSet` instance that this builder commits to.
|
||
|
BL_INLINE BLBitSet* bitSet() const noexcept { return static_cast<BLBitSet*>(_bitSet); }
|
||
|
|
||
|
//! \}
|
||
|
|
||
|
//! \name Builder Interface
|
||
|
//! \{
|
||
|
|
||
|
BL_INLINE BLResult reset() noexcept {
|
||
|
_bitSet = nullptr;
|
||
|
_areaShift = kAreaShift;
|
||
|
_areaIndex = kInvalidAreaIndex;
|
||
|
return BL_SUCCESS;
|
||
|
}
|
||
|
|
||
|
BL_INLINE BLResult reset(BLBitSetCore* bitSet) noexcept {
|
||
|
_bitSet = bitSet;
|
||
|
_areaShift = kAreaShift;
|
||
|
_areaIndex = kInvalidAreaIndex;
|
||
|
return BL_SUCCESS;
|
||
|
}
|
||
|
|
||
|
//! Adds a bit to the area maintained by BitSet builder.
|
||
|
//!
|
||
|
//! If the area of `bitIndex` is different compared to the current active area, the current area will be
|
||
|
//! committed to the BitSet. This is actually the only operation that can return \ref BL_ERROR_OUT_OF_MEMORY.
|
||
|
BL_INLINE BLResult addBit(uint32_t bitIndex) noexcept {
|
||
|
uint32_t areaIndex = bitIndex / kAreaBitCount;
|
||
|
if (_areaIndex != areaIndex)
|
||
|
BL_PROPAGATE(blBitSetBuilderCommit(_bitSet, this, areaIndex));
|
||
|
|
||
|
bitIndex &= kAreaBitCount - 1;
|
||
|
_areaWords[bitIndex / 32u] |= uint32_t(0x80000000u) >> (bitIndex % 32u);
|
||
|
return BL_SUCCESS;
|
||
|
}
|
||
|
|
||
|
//! Adds a `[startBit, endBit)` range of bits to the BitSet.
|
||
|
//!
|
||
|
//! If the range is relatively small and fits into a single builder area, it will be added to that area.
|
||
|
//! On the other hand, if the range is large, the area will be kept and the builder would call \ref
|
||
|
//! BLBitSet::addRange() instead. If the are of the range is different compared to the current active area,
|
||
|
//! the data in the current active area will be committed.
|
||
|
BL_INLINE BLResult addRange(uint32_t startBit, uint32_t endBit) noexcept {
|
||
|
return blBitSetBuilderAddRange(_bitSet, this, startBit, endBit);
|
||
|
}
|
||
|
|
||
|
//! Commits changes in the current active area to the BitSet.
|
||
|
//!
|
||
|
//! \note This must be called in order to finalize building the BitSet. If this function is not called the
|
||
|
//! BitSet could have missing bits that are in the current active area.
|
||
|
BL_INLINE BLResult commit() noexcept {
|
||
|
return blBitSetBuilderCommit(_bitSet, this, kInvalidAreaIndex);
|
||
|
}
|
||
|
|
||
|
//! Similar to \ref commit(), but the additional parameter `newAreaIndex` will be used to set the current
|
||
|
//! active area.
|
||
|
BL_INLINE BLResult commit(uint32_t newAreaIndex) noexcept {
|
||
|
return blBitSetBuilderCommit(_bitSet, this, newAreaIndex);
|
||
|
}
|
||
|
|
||
|
//! \}
|
||
|
};
|
||
|
|
||
|
//! BitSet builder [C++ API] that is configured to have a temporary storage of 512 bits.
|
||
|
using BLBitSetBuilder = BLBitSetBuilderT<512>;
|
||
|
|
||
|
//! BitSet word iterator [C++ API].
|
||
|
//!
|
||
|
//! Low-level iterator that sees a BitSet as an array of bit words. It only iterates non-zero words and
|
||
|
//! returns zero at the end of iteration.
|
||
|
//!
|
||
|
//! A simple way of printing all non-zero words of a BitWord:
|
||
|
//!
|
||
|
//! ```
|
||
|
//! BLBitSet set;
|
||
|
//! set.addRange(100, 200);
|
||
|
//!
|
||
|
//! BLBitSetWordIterator it(set);
|
||
|
//! while (uint32_t bits = it.nextWord()) {
|
||
|
//! printf("{WordIndex: %u, WordData: %08X }\n", it.wordIndex(), bits);
|
||
|
//! }
|
||
|
//! ```
|
||
|
class BLBitSetWordIterator final {
|
||
|
public:
|
||
|
//! \name Members
|
||
|
//! \{
|
||
|
|
||
|
const BLBitSetSegment* _segmentPtr;
|
||
|
const BLBitSetSegment* _segmentEnd;
|
||
|
BLBitSetData _data;
|
||
|
uint32_t _wordIndex;
|
||
|
|
||
|
//! \}
|
||
|
|
||
|
//! \name Construction & Destruction
|
||
|
//! \{
|
||
|
|
||
|
//! Creates a default constructed iterator, not initialized to iterate any BitSet.
|
||
|
BL_INLINE BLBitSetWordIterator() noexcept { reset(); }
|
||
|
//! Creates an iterator, that will iterate the given `bitSet`.
|
||
|
//!
|
||
|
//! \note The `bitSet` cannot change or be destroyed during iteration.
|
||
|
BL_INLINE BLBitSetWordIterator(const BLBitSetCore& bitSet) noexcept { reset(bitSet); }
|
||
|
//! Creates a copy of `other` iterator.
|
||
|
BL_INLINE BLBitSetWordIterator(const BLBitSetWordIterator& other) noexcept = default;
|
||
|
|
||
|
//! \}
|
||
|
|
||
|
//! \name Overloaded Operators
|
||
|
//! \{
|
||
|
|
||
|
BL_INLINE BLBitSetWordIterator& operator=(const BLBitSetWordIterator& other) noexcept = default;
|
||
|
|
||
|
//! \}
|
||
|
|
||
|
//! \name Reset
|
||
|
//! \{
|
||
|
|
||
|
//! Resets the iterator (puts it into a default constructed state).
|
||
|
BL_INLINE void reset() noexcept {
|
||
|
_segmentPtr = nullptr;
|
||
|
_segmentEnd = nullptr;
|
||
|
_data.reset();
|
||
|
_wordIndex = 0;
|
||
|
}
|
||
|
|
||
|
//! Reinitializes the iterator to iterate the given `bitSet`, from the beginning.
|
||
|
BL_INLINE void reset(const BLBitSetCore& bitSet) noexcept {
|
||
|
blBitSetGetData(&bitSet, &_data);
|
||
|
_segmentPtr = _data.segmentData;
|
||
|
_segmentEnd = _data.segmentData + _data.segmentCount;
|
||
|
_wordIndex = _segmentPtr != _segmentEnd ? uint32_t(_segmentPtr->startWord() - 1u) : uint32_t(0xFFFFFFFFu);
|
||
|
}
|
||
|
|
||
|
//! \}
|
||
|
|
||
|
//! \name Iterator Interface
|
||
|
//! \{
|
||
|
|
||
|
//! Returns the next (or the first, if called the first time) non-zero word of the BitSet or zero if the
|
||
|
//! iteration ended.
|
||
|
//!
|
||
|
//! Use `wordIndex()` to get the index (in word units) of the word returned.
|
||
|
BL_INLINE uint32_t nextWord() noexcept {
|
||
|
if (_segmentPtr == _segmentEnd)
|
||
|
return 0;
|
||
|
|
||
|
_wordIndex++;
|
||
|
for (;;) {
|
||
|
if (_segmentPtr->allOnes()) {
|
||
|
if (_wordIndex < _segmentPtr->_rangeEndWord())
|
||
|
return 0xFFFFFFFFu;
|
||
|
}
|
||
|
else {
|
||
|
uint32_t endWord = _segmentPtr->_denseEndWord();
|
||
|
while (_wordIndex < endWord) {
|
||
|
uint32_t bits = _segmentPtr->_data[_wordIndex & (BL_BIT_SET_SEGMENT_WORD_COUNT - 1u)];
|
||
|
if (bits != 0u)
|
||
|
return bits;
|
||
|
_wordIndex++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (++_segmentPtr == _segmentEnd)
|
||
|
return 0;
|
||
|
_wordIndex = _segmentPtr->startWord();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//! Returns the current bit index of a word returned by `nextWord()`.
|
||
|
BL_INLINE uint32_t bitIndex() const noexcept { return _wordIndex * 32u; }
|
||
|
|
||
|
//! Returns the current word index of a word returned by `nextWord()`.
|
||
|
BL_INLINE uint32_t wordIndex() const noexcept { return _wordIndex; }
|
||
|
|
||
|
//! \}
|
||
|
};
|
||
|
|
||
|
#endif
|
||
|
//! \}
|
||
|
|
||
|
//! \}
|
||
|
|
||
|
#endif // BLEND2D_BITSET_H
|