1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
|
/* Copyright (c) 2008-2022 the MRtrix3 contributors.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Covered Software is provided under this License on an "as is"
* basis, without warranty of any kind, either expressed, implied, or
* statutory, including, without limitation, warranties that the
* Covered Software is free of defects, merchantable, fit for a
* particular purpose or non-infringing.
* See the Mozilla Public License v. 2.0 for more details.
*
* For more details, see http://www.mrtrix.org/.
*/
#ifndef __misc_bitset_h__
#define __misc_bitset_h__
#include <atomic>
#include <stdint.h>
#include "mrtrix.h"
namespace MR {
//! a class for storing bitwise information
/*! The BitSet class stores information in a bitwise fashion. Only a single
* bit of memory is used for each bit of information. Unlike the std::bitset
* class, the size of the BitSet can be specified (and modified) at runtime.
*
* This class is useful for storing a single boolean value (true or false)
* for each element of some vector. It may also be useful for two-dimensional
* data, though the programmer is responsible for performing the conversion
* from a two-dimensional position to an array index. If a boolean value for
* each voxel is required for three- or four-dimensional data, use of an
* Image::BufferScratch<bool> is recommended. */
class BitSet { NOMEMALIGN
public:
/*! convenience classes that allow the programmer to access and modify
* the bit wise information using the [] operator. */
class Value { NOMEMALIGN
public:
Value (BitSet& master, const size_t offset) : d (master), p (offset) { assert (p < d.size()); }
operator bool() const { return d.test (p); }
bool operator== (const bool i) const { return (i == d.test (p)); }
bool operator= (const bool i) { i ? d.set (p) : d.reset (p); return i; }
Value& operator|= (const bool i) { (i || bool(*this)) ? d.set (p) : d.reset (p); return *this; }
Value& operator&= (const bool i) { (i && bool(*this)) ? d.set (p) : d.reset (p); return *this; }
friend std::ostream& operator<< (std::ostream& stream, const Value& V) { stream << (bool(V) ? '1' : '0'); return stream; }
private:
BitSet& d;
const size_t p;
};
class ConstValue { NOMEMALIGN
public:
ConstValue (const BitSet& master, const size_t offset) : d (master), p (offset) { assert (p < d.size()); }
operator bool() const { return d.test (p); }
bool operator== (const bool i) const { return (i == d.test (p)); }
friend std::ostream& operator<< (std::ostream& stream, const ConstValue& V) { stream << (bool(V) ? '1' : '0'); return stream; }
private:
const BitSet& d;
const size_t p;
};
//! create a new bitset with the desired size.
/*! If \a allocator is unspecified or set to false, the initial
* value for each element will be false. If it is specified as true,
* then all data will be initialised as true. */
BitSet (const size_t, const bool allocator = false);
//! copy-construct a bitset, with explicit copying of data into the new instance
/*! The BitSet copy-constructor explicitly copies all of the data from the
* constructing instance into the new instance. Therefore, their sizes and data
* will be identical, but subsequent modifications to the data in one instance
* will not affect the other. */
BitSet (const BitSet&);
~BitSet();
//! resize the bitset, retaining existing data
/*! Modify the size of the BitSet. Existing data information will be retained
* by the resizing process. If the new size is smaller than the existing size,
* then all excess data will be truncated. If the new size is larger than the
* existing size, then all existing data will be retained, and additional
* bits beyond the previous size will be set to false; unless \a allocator is
* explicitly provided as true, in which case all additional bits will be set
* to true. */
void resize (const size_t, const bool allocator = false);
//! clear the data
/*! Clears all existing data in the BitSet. By default all values will be set
* to false; if \a allocator is explicitly set to true, then all values will
* be set to true. */
void clear (const bool allocator = false);
//! access boolean value at a given index
/*! These functions provide access to the raw boolean data using the []
* (square-bracket) operator. Both const and non-const versions are provided.
* Although internally the BitSet class stores eight boolean values in each
* byte of memory (to minimise memory usage), these operators can be used to
* access and manipulate the bit wise data without corrupting the surrounding
* data.
* \returns a Value or ConstValue class used to manipulate the bit data at
* the specified index */
ConstValue operator[] (const size_t i) const { assert (i < bits); return ConstValue (*this, i); }
Value operator[] (const size_t i) { assert (i < bits); return Value (*this, i); }
//! the number of boolean elements in the set
/*! The size of the BitSet. Note that this is the number of boolean values
* stored in the array; NOT the memory consumption of the class.
* \returns the number of boolean elements in the BitSet. */
size_t size() const { return bits; }
//! whether or not the bitset is 'full' i.e. all elements are true
/*! Convenience function for testing whether or not the BitSet is 'full',
* i.e. all elements in the array are set to true. This can be useful if
* the programmer chooses not to manually keep track of the number of
* entries set or not set. Because it processes the data in bytes rather
* than bits, it is faster than the programmer manually performing this
* calculation.
* \returns true if all elements are set to true, false otherwise. */
bool full() const;
//! whether or not the bitset is 'empty' i.e. all elements are false
/*! Convenience function for testing whether or not the BitSet is 'empty',
* i.e. all elements in the array are set to false. This can be useful if
* the programmer chooses not to manually keep track of the number of
* entries set or not set. Because it processes the data in bytes rather
* than bits, it is faster than the programmer manually performing this
* calculation.
* \returns true if all elements are set to false, false otherwise. */
bool empty() const;
//! count the number of true entries in the set
/*! Convenience function for counting the number of true entries in the
* set. This can be useful if the programmer chooses not to manually keep
* track of the number of entries set or not set. The number of entries
* in the data that are set to false can be calculated as:
* \code
* BitSet data (1000);
* // ...
* const size_t false_count = data.size() - data.count();
* \endcode
* \returns the number of elements in the array set to true. */
size_t count() const;
//! convenience functions for performing boolean operations
/*! Convenience function for performing boolean operations using BitSet
* data. Each of these functions performs a particular boolean operation,
* but for all of the data in the array. Because they process the data
* in bytes rather than bits, they are faster than if the programmer
* manually performed these operations on a per-bit basis.
*
* Particular notes of interest:
* - The '=' (assignment) operator will copy both the size of the
* passed BitSet, and the data itself.
* - The '==' (comparison) operator will return false if the two BitSets
* differ in their number of bits. If the programmer wishes to compare
* two BitSets of different sizes, where only the length of the smaller
* BitSet is considered, this can be achieved as follows:
* \code
* BitSet A (1000), B (2000);
* // ...
* BitSet B_small (B);
* B_small.resize (A.size());
* if (A == B_small) {
* // Do something
* }
* \endcode
* */
BitSet& operator= (const BitSet&);
bool operator== (const BitSet&) const;
bool operator!= (const BitSet&) const;
BitSet& operator|= (const BitSet&);
BitSet& operator&= (const BitSet&);
BitSet& operator^= (const BitSet&);
BitSet operator| (const BitSet&) const;
BitSet operator& (const BitSet&) const;
BitSet operator^ (const BitSet&) const;
BitSet operator~ () const;
const uint8_t* get_data_ptr() const { return data; }
friend std::ostream& operator<< (std::ostream&, const BitSet&);
protected:
size_t bits;
size_t bytes;
bool have_excess_bits() const { return (bits & size_t(7)); }
size_t excess_bits() const { return (8*bytes - bits); }
uint8_t excess_bit_mask() const { assert (have_excess_bits()); return 0xFF << (8-excess_bits()); }
bool test (const size_t index) const
{
assert (index < bits);
return (data[index>>3] & masks[index&size_t(7)]);
}
void set (const size_t index)
{
assert (index < bits);
std::atomic<uint8_t>* at = reinterpret_cast<std::atomic<uint8_t>*> (((uint8_t*) data) + (index>>3));
uint8_t prev = *at, new_value;
do { new_value = prev | masks[index&size_t(7)]; } while (!at->compare_exchange_weak (prev, new_value));
}
void reset (const size_t index)
{
assert (index < bits);
std::atomic<uint8_t>* at = reinterpret_cast<std::atomic<uint8_t>*> (((uint8_t*) data) + (index>>3));
uint8_t prev = *at, new_value;
do { new_value = prev & ~masks[index&size_t(7)]; } while (!at->compare_exchange_weak (prev, new_value));
}
private:
uint8_t* data;
static const uint8_t masks[8];
};
}
#endif
|