#pragma once
#include <vector>
#include <atomic>
#include <cassert>
#include <cstddef>
#include <limits>
#include "iterator_tpl.h"
namespace atomicbitvector {
template <typename T>
struct atomwrapper {
std::atomic<T> _a;
atomwrapper() : _a() { }
atomwrapper(const std::atomic<T> &a) : _a(a.load()) { }
atomwrapper(const atomwrapper &other) : _a(other._a.load()) { }
atomwrapper &operator=(const atomwrapper &other) {
_a.store(other._a.load());
}
std::atomic<T>& ref(void) { return _a; }
const std::atomic<T>& const_ref(void) const { return _a; }
};
class atomic_bv_t {
public:
atomic_bv_t(size_t N) : _size(N),
kNumBlocks((N + kBitsPerBlock - 1) / kBitsPerBlock) {
data_.resize(kNumBlocks);
}
bool set(size_t idx, std::memory_order order = std::memory_order_seq_cst);
bool reset(size_t idx, std::memory_order order = std::memory_order_seq_cst);
bool set(
size_t idx,
bool value,
std::memory_order order = std::memory_order_seq_cst);
bool test(size_t idx, std::memory_order order = std::memory_order_seq_cst)
const;
bool operator[](size_t idx) const;
constexpr size_t size() const {
return _size;
}
struct it_state {
size_t pos;
inline void next(const atomic_bv_t* ref) { ++pos; while (pos < ref->size() && !ref->test(pos)) { ++pos; } }
inline void prev(const atomic_bv_t* ref) { --pos; while (pos > 0 && !ref->test(pos)) { --pos; } }
inline void begin(const atomic_bv_t* ref) { pos = 0; while (pos < ref->size() && !ref->test(pos)) { ++pos; } }
inline void end(const atomic_bv_t* ref) { pos = ref->size(); }
inline size_t get(atomic_bv_t* ref) { return pos; }
inline const size_t get(const atomic_bv_t* ref) const { return ref->test(pos); }
inline bool cmp(const it_state& s) const { return pos != s.pos; }
};
SETUP_ITERATORS(atomic_bv_t, size_t, it_state);
SETUP_REVERSE_ITERATORS(atomic_bv_t, size_t, it_state);
private:
#if (ATOMIC_LLONG_LOCK_FREE == 2)
typedef unsigned long long BlockType;
#elif (ATOMIC_LONG_LOCK_FREE == 2)
typedef unsigned long BlockType;
#else
typedef unsigned int BlockType;
#endif
typedef atomwrapper<BlockType> AtomicBlockType;
static constexpr size_t kBitsPerBlock =
std::numeric_limits<BlockType>::digits;
static constexpr size_t blockIndex(size_t bit) {
return bit / kBitsPerBlock;
}
static constexpr size_t bitOffset(size_t bit) {
return bit % kBitsPerBlock;
}
static constexpr BlockType kOne = 1;
size_t _size; size_t kNumBlocks; std::vector<AtomicBlockType> data_; };
inline bool atomic_bv_t::set(size_t idx, std::memory_order order) {
assert(idx < _size);
BlockType mask = kOne << bitOffset(idx);
return data_[blockIndex(idx)].ref().fetch_or(mask, order) & mask;
}
inline bool atomic_bv_t::reset(size_t idx, std::memory_order order) {
assert(idx < _size);
BlockType mask = kOne << bitOffset(idx);
return data_[blockIndex(idx)].ref().fetch_and(~mask, order) & mask;
}
inline bool atomic_bv_t::set(size_t idx, bool value, std::memory_order order) {
return value ? set(idx, order) : reset(idx, order);
}
inline bool atomic_bv_t::test(size_t idx, std::memory_order order) const {
assert(idx < _size);
BlockType mask = kOne << bitOffset(idx);
return data_[blockIndex(idx)].const_ref().load(order) & mask;
}
inline bool atomic_bv_t::operator[](size_t idx) const {
return test(idx);
}
}