/* Quorum
* Copyright (C) 2012 Genome group at University of Maryland.
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __JELLYFISH_ATOMIC_BITS_ARRAY_HPP__
#define __JELLYFISH_ATOMIC_BITS_ARRAY_HPP__
#include
#include
#include
#include
#include
namespace jellyfish {
template
class atomic_bits_array_base {
static const int w_ = sizeof(T) * 8;
const int bits_;
const size_t size_;
const T mask_;
const jflib::divisor64 d_;
size_t size_bytes_;
T* data_;
static atomic::gcc atomic_;
friend class iterator;
class iterator : public std::iterator {
friend class atomic_bits_array_base;
const atomic_bits_array_base& ary_;
T* word_;
T mask_;
int off_;
iterator(const atomic_bits_array_base& a, T* w, T m, int o) : ary_(a), word_(w), mask_(m), off_(o) { }
public:
bool operator==(const iterator& rhs) const { return word_ == rhs.word_ && off_ == rhs.off_; }
bool operator!=(const iterator& rhs) const { return word_ != rhs.word_ || off_ != rhs.off_; }
Value operator*() const { return static_cast((*word_ & mask_) >> off_); }
Value* operator->() const { return 0; }
iterator& operator++() {
off_ += ary_.bits_;
if(off_ + ary_.bits_ < w_) {
mask_ <<= ary_.bits_;
} else {
++word_;
mask_ = ary_.mask_;
off_ = 0;
}
return *this;
}
iterator operator++(int) {
iterator res(*this);
++*this;
return res;
}
};
class element_proxy {
T* word_;
const T mask_;
const int off_;
T prev_word_;
Value get_val(T v) const {
return static_cast((v & mask_) >> off_);
}
public:
element_proxy(T* word, T mask, int off) :
word_(word), mask_(mask), off_(off)
{ }
operator Value() const { return get_val(*word_); }
Value get() {
prev_word_ = *word_;
return get_val(prev_word_);
}
bool set(Value& nval) {
Value pval;
Value cval = get_val(prev_word_);
do {
pval = cval;
const T new_word = (prev_word_ & ~mask_) | ((static_cast(nval) << off_) & mask_);
const T actual_word = atomic_.cas(word_, prev_word_, new_word);
if(__builtin_expect(actual_word == prev_word_, 1))
return true;
prev_word_ = actual_word;
cval = get_val(prev_word_);
} while(pval == cval);
nval = cval;
return false;
}
};
public:
atomic_bits_array_base(int bits, // Number of bits per entry
size_t size) : // Number of entries
bits_(bits),
size_(size),
mask_((T)-1 >> (w_ - bits)), // mask of one entry at the LSB of a word
d_(w_ / bits), // divisor of the number of entries per word
size_bytes_((size / d_ + (size % d_ != 0)) * sizeof(T)),
data_(static_cast(this)->alloc_data(size_bytes_))
{
static_assert(sizeof(T) >= sizeof(Value), "Container type T must have at least as many bits as value type");
if((size_t)bits > sizeof(Value) * 8)
throw std::runtime_error("The number of bits per entry must be less than the number of bits in the value type");
if(!data_)
throw std::runtime_error("Can't allocate memory for atomic_bits_array");
}
// Return the element at position pos. No check for out of bounds.
element_proxy operator[](size_t pos) {
uint64_t q, r;
d_.division(pos, q, r);
const int off = r * bits_;
return element_proxy(data_ + q, mask_ << off, off);
}
const element_proxy operator[](size_t pos) const {
uint64_t q, r;
d_.division(pos, q, r);
const int off = r * bits_;
return element_proxy(data_ + q, mask_ << off, off);
}
void write(std::ostream& os) const {
os.write((const char*)data_, size_bytes_);
}
size_t size_bytes() const { return size_bytes_; }
int bits() const { return bits_; }
iterator begin() const { return iterator(*this, data_, mask_, 0); }
iterator end() const {
uint64_t q, r;
d_.division(size_, q, r);
const int off = r * bits_;
return iterator(*this, data_ + q, mask_ << off, off);
}
};
template
class atomic_bits_array :
protected allocators::mmap,
public atomic_bits_array_base >
{
typedef atomic_bits_array_base > super;
friend class atomic_bits_array_base >;
public:
atomic_bits_array(int bits, size_t size) :
allocators::mmap(),
super(bits, size)
{ }
protected:
T* alloc_data(size_t s) {
allocators::mmap::realloc(s);
return (T*)allocators::mmap::get_ptr();
}
};
struct mem_info {
void* ptr_;
size_t bytes_;
mem_info(void* ptr, size_t bytes) : ptr_(ptr), bytes_(bytes) { }
};
template
class atomic_bits_array_raw :
protected mem_info,
public atomic_bits_array_base >
{
typedef atomic_bits_array_base > super;
friend class atomic_bits_array_base >;
public:
atomic_bits_array_raw(void* ptr, size_t bytes, int bits, size_t size) :
mem_info(ptr, bytes),
super(bits, size)
{ }
protected:
T* alloc_data(size_t s) {
assert(bytes_ == s);
return (T*)ptr_;
}
};
} // namespace jellyfish
#endif /* __JELLYFISH_ATOMIC_BITS_ARRAY_HPP__ */