Commit 1f6733d9 authored by Mark Olesen's avatar Mark Olesen
Browse files

PackedList - activated lazy evaluation

- moving back to original flat addressing in iterators means there is no
  performance issue with using lazy evaluation
- set() method now has ~0 for a default value.
  We can thus simply write 'set(i) to trun on all of the bits.
  This means we can use it just like labelHashSet::set(i)
- added flip() method for inverting bits. I don't know where we might need
  it, but the STL has it so we might as well too.
parent f0f677e3
......@@ -52,13 +52,25 @@ int main(int argc, char *argv[])
list1 = -1;
list1.print(Info);
Info<< "\ntest zero\n";
list1 = 0;
list1.print(Info);
Info<< "\ntest set() with default argument (max_value)\n";
list1.set(3);
list1.print(Info);
Info<< "\ntest assign between references\n";
list1[2] = 3;
list1[4] = list1[2];
list1.print(Info);
Info<< "\ntest assign between references, with chaining\n";
list1[4] = list1[2] = 1;
list1[0] = list1[4] = 1;
list1.print(Info);
Info<< "\ntest assign between references, with chaining and auto-vivify\n";
list1[1] = list1[8] = list1[10] = list1[14] = 2;
list1.print(Info);
{
......@@ -91,6 +103,14 @@ int main(int argc, char *argv[])
list1.resize(8, list1.max_value());
list1.print(Info);
Info<< "\ntest flip() function\n";
list1.flip();
list1.print(Info);
Info<< "\nre-flip()\n";
list1.flip();
list1.print(Info);
Info<< "\ntest set() function\n";
list1.set(1, 5);
list1.print(Info);
......@@ -188,15 +208,23 @@ int main(int argc, char *argv[])
{
Info<< "\ntest assignment of iterator\n";
list1.print(Info);
PackedList<3>::iterator cit = list1[25];
cit.print(Info);
Info<< "cend()\n";
list1.end().print(Info);
PackedList<3>::iterator cit = list1[100];
Info<< "out-of-range: ";
cit.print(Info);
cit = list1[15];
Info<< "in-range: ";
cit.print(Info);
Info<< "out-of-range: ";
cit = list1[1000];
cit.print(Info);
}
for
(
PackedList<3>::iterator cit = list1[5];
PackedList<3>::iterator cit = list1[30];
cit != list1.end();
++cit
)
......@@ -204,14 +232,19 @@ int main(int argc, char *argv[])
cit.print(Info);
}
// Info<< "\ntest operator[] auto-vivify\n";
// const unsigned int val = list1[45];
//
// Info<< "list[45]:" << val << "\n";
// list1[45] = list1.max_value();
// Info<< "list[45]:" << list1[45] << "\n";
// list1[49] = list1.max_value();
// list1.print(Info);
Info<< "\ntest operator[] auto-vivify\n";
Info<< "size:" << list1.size() << "\n";
const unsigned int val = list1[45];
Info<< "list[45]:" << val << "\n";
Info<< "size after read:" << list1.size() << "\n";
list1[45] = list1.max_value();
Info<< "size after write:" << list1.size() << "\n";
Info<< "list[45]:" << list1[45] << "\n";
list1[49] = list1[100];
list1.print(Info);
Info<< "\ntest copy constructor + append\n";
......
......@@ -83,25 +83,25 @@ unsigned int Foam::PackedList<nBits>::count() const
if (size_)
{
// mask value for complete chunks
// mask value for complete segments
unsigned int mask = maskLower(packing());
unsigned int endIdx = size_ / packing();
unsigned int endOff = size_ % packing();
const unsigned int endSeg = size_ / packing();
const unsigned int endOff = size_ % packing();
// count bits in complete elements
for (unsigned i = 0; i < endIdx; ++i)
// count bits in complete segments
for (unsigned i = 0; i < endSeg; ++i)
{
register unsigned int bits = StorageList::operator[](i) & mask;
COUNT_PACKEDBITS(c, bits);
}
// count bits in partial chunk
// count bits in partial segment
if (endOff)
{
mask = maskLower(endOff);
register unsigned int bits = StorageList::operator[](endIdx) & mask;
register unsigned int bits = StorageList::operator[](endSeg) & mask;
COUNT_PACKEDBITS(c, bits);
}
}
......@@ -118,7 +118,7 @@ bool Foam::PackedList<nBits>::trim()
return false;
}
// mask value for complete chunks
// mask value for complete segments
unsigned int mask = maskLower(packing());
label currElem = packedLength(size_) - 1;
......@@ -130,7 +130,7 @@ bool Foam::PackedList<nBits>::trim()
StorageList::operator[](currElem) &= maskLower(endOff);
}
// test entire chunk
// test entire segment
while (currElem > 0 && !(StorageList::operator[](currElem) &= mask))
{
currElem--;
......@@ -162,10 +162,22 @@ bool Foam::PackedList<nBits>::trim()
}
template<unsigned nBits>
void Foam::PackedList<nBits>::flip()
{
label packLen = packedLength(size_);
for (label i=0; i < packLen; i++)
{
StorageList::operator[](i) = ~StorageList::operator[](i);
}
}
template<unsigned nBits>
Foam::labelList Foam::PackedList<nBits>::values() const
{
labelList elems(size());
labelList elems(size_);
forAll(*this, i)
{
......@@ -178,10 +190,11 @@ Foam::labelList Foam::PackedList<nBits>::values() const
template<unsigned nBits>
Foam::Ostream& Foam::PackedList<nBits>::iteratorBase::print(Ostream& os) const
{
os << "iterator<" << label(nBits) << "> ["
<< (index_ * packing() + offset_) << "]"
<< " index:" << index_ << " offset:" << offset_
<< " value:" << unsigned(*this)
os << "iterator<" << label(nBits) << "> ["
<< this->index_ << "]"
<< " segment:" << label(this->index_ / packing())
<< " offset:" << label(this->index_ % packing())
<< " value:" << this->get()
<< nl;
return os;
......@@ -194,7 +207,7 @@ Foam::Ostream& Foam::PackedList<nBits>::print(Ostream& os) const
os << "PackedList<" << label(nBits) << ">"
<< " max_value:" << max_value()
<< " packing:" << packing() << nl
<< "values: " << size() << "/" << capacity() << "( ";
<< "values: " << size_ << "/" << capacity() << "( ";
forAll(*this, i)
{
os << get(i) << ' ';
......@@ -205,17 +218,17 @@ Foam::Ostream& Foam::PackedList<nBits>::print(Ostream& os) const
os << ")\n"
<< "storage: " << packLen << "/" << StorageList::size() << "( ";
// mask value for complete chunks
// mask value for complete segments
unsigned int mask = maskLower(packing());
for (label i=0; i < packLen; i++)
{
const StorageType& rawBits = StorageList::operator[](i);
// the final storage may not be full, modify mask accordingly
// the final segment may not be full, modify mask accordingly
if (i+1 == packLen)
{
unsigned endOff = size_ % packing();
unsigned int endOff = size_ % packing();
if (endOff)
{
......@@ -229,7 +242,7 @@ Foam::Ostream& Foam::PackedList<nBits>::print(Ostream& os) const
for (unsigned int testBit = (1 << max_bits()); testBit; testBit >>= 1)
{
if (testBit & mask)
if (mask & testBit)
{
if (rawBits & testBit)
{
......
......@@ -44,9 +44,9 @@ Note
Using the iteratorBase as a proxy allows assignment of values
between list elements. Thus the following bit of code works as expected:
@code
blist[1] = blist[5]; // value assignment, not iterator position
blist[2] = blist[5] = 4; // propagates value
blist[1] = blist[5] = blist[6]; // propagates value
list[1] = list[5]; // value assignment, not iterator position
list[2] = list[5] = 4; // propagates value
list[1] = list[5] = list[6]; // propagates value
@endcode
Using get() or the '[]' operator are similarly fast. Looping and reading
......@@ -58,11 +58,21 @@ Note
useful for branching on changed values.
@code
blist[5] = 4;
changed = blist.set(5, 8);
list[5] = 4;
changed = list.set(5, 8);
if (changed) ...
@endcode
The lazy evaluation used means that reading an out-of-range element
returns zero, but does not affect the list size. Even in a non-const
context, only the assigment causes the element to be created.
For example,
@code
list.resize(4);
Info<< list[10] << "\n"; // print zero, but doesn't adjust list
list[8] = 1;
@endcode
SeeAlso
Foam::DynamicList
......@@ -120,9 +130,6 @@ class PackedList
//- Calculate the list length when packed
inline static label packedLength(const label);
//- Check index I is within valid range [ 0 .. max_value() ]
inline void checkIndex(const label) const;
public:
// Public data
......@@ -172,22 +179,23 @@ public:
// Access
//- The number of elements that can be stored before resizing
//- The number of elements that can be stored before reallocating
inline label capacity() const;
//- Number of entries.
inline label size() const;
//- Return true if the list is empty (i.e., if size() == 0).
//- Return true if the list is empty (i.e., size() == 0).
inline bool empty() const;
//- Get value at index I.
// Does not auto-vivify entries.
// Never auto-vivify entries.
inline unsigned int get(const label) const;
//- Set value at index I. Return true if value changed.
// Does not auto-vivify entries.
inline bool set(const label, const unsigned int val);
// Does auto-vivify for non-existent entries.
// Default value set is the max_value.
inline bool set(const label, const unsigned int val = ~0u);
//- Return the underlying packed storage
inline List<unsigned int>& storage();
......@@ -200,9 +208,6 @@ public:
// http://en.wikipedia.org/wiki/Hamming_weight
unsigned int count() const;
//- Trim any trailing zero elements
bool trim();
//- Return the values as a labelList
labelList values() const;
......@@ -211,6 +216,12 @@ public:
// Edit
//- Trim any trailing zero elements
bool trim();
//- Invert the bits in the addressable region.
void flip();
//- Alter the size of the underlying storage.
// The addressed size will be truncated if needed to fit, but will
// remain otherwise untouched.
......@@ -255,12 +266,12 @@ public:
inline unsigned int remove();
//- Get value at index I
// Does not auto-vivify entries.
// Never auto-vivify entries.
inline unsigned int operator[](const label) const;
//- Set value at index I.
// Returns iterator to perform the actual operation.
// Does not auto-vivify entries.
// Does not auto-vivify entries, but will when assigned to.
inline iteratorBase operator[](const label);
//- Assignment of all entries to the given value. Takes linear time.
......@@ -294,52 +305,27 @@ public:
// This also lets us use the default bitwise copy/assignment
PackedList* list_;
//- Element index within storage
unsigned index_;
//- Offset within storage element
unsigned offset_;
//- Element index
label index_;
// Protected Member Functions
//- Get value as unsigned
//- Get value as unsigned, no range-checking
inline unsigned int get() const;
//- Set value, returning true if changed
//- Set value, returning true if changed, no range-checking
inline bool set(unsigned int);
//- Increment to new position
inline void incr();
//- Decrement to new position
inline void decr();
//- Move to new position, but not beyond end()
inline void seek(const iteratorBase&);
// Constructors
//- Construct null
inline iteratorBase();
//- Copy construct
inline iteratorBase(const iteratorBase&);
//- Construct from base list and position index
inline iteratorBase(const PackedList*, const label);
public:
// Member Functions
//- Return true if the element is within addressable range
inline bool valid() const;
//- Move iterator to end() if it would otherwise be out-of-range
// Returns true if the element was already ok
inline bool validate();
// Member Operators
//- Compare positions
......@@ -350,10 +336,12 @@ public:
// This allows packed[0] = packed[3] for assigning values
inline unsigned int operator=(const iteratorBase&);
//- Assign value
//- Assign value.
// A non-existent entry will be auto-vivified.
inline unsigned int operator=(const unsigned int val);
//- Conversion operator
// Never auto-vivify entries.
inline operator unsigned int () const;
//- Print value and information
......@@ -378,6 +366,8 @@ public:
inline iterator();
//- Construct from iterator base, eg iter(packedlist[i])
// but also "iterator iter = packedlist[i];"
// An out-of-range iterator is assigned end()
inline iterator(const iteratorBase&);
//- Construct from base list and position index
......@@ -386,6 +376,7 @@ public:
// Member Operators
//- Assign from iteratorBase, eg iter = packedlist[i]
// An out-of-range iterator is assigned end()
inline iterator& operator=(const iteratorBase&);
//- Return value
......@@ -427,7 +418,9 @@ public:
//- Construct null
inline const_iterator();
//- Construct from iterator base
//- Construct from iterator base, eg iter(packedlist[i])
// but also "const_iterator iter = packedlist[i];"
// An out-of-range iterator is assigned end()
inline const_iterator(const iteratorBase&);
//- Construct from base list and position index
......@@ -439,7 +432,7 @@ public:
// Member operators
//- Assign from iteratorBase or derived
// eg, iter = packedlist[i] or iter = [non-const]list.begin()
// eg, iter = packedlist[i] or even iter = list.begin()
inline const_iterator& operator=(const iteratorBase&);
//- Return referenced value directly
......
......@@ -65,24 +65,6 @@ inline Foam::label Foam::PackedList<nBits>::packedLength(const label nElem)
}
template<unsigned nBits>
inline void Foam::PackedList<nBits>::checkIndex(const label i) const
{
if (!size_)
{
FatalErrorIn("PackedList<nBits>::checkIndex(const label)")
<< "attempt to access element from zero-sized list"
<< abort(FatalError);
}
else if (i < 0 || i >= size_)
{
FatalErrorIn("PackedList<nBits>::checkIndex(const label)")
<< "index " << i << " out of range 0 ... " << size_-1
<< abort(FatalError);
}
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
template<unsigned nBits>
......@@ -105,7 +87,7 @@ template<unsigned nBits>
inline Foam::PackedList<nBits>::PackedList(const PackedList<nBits>& lst)
:
StorageList(lst),
size_(lst.size())
size_(lst.size_)
{}
......@@ -132,20 +114,7 @@ template<unsigned nBits>
inline Foam::PackedList<nBits>::iteratorBase::iteratorBase()
:
list_(0),
index_(0),
offset_(0)
{}
template<unsigned nBits>
inline Foam::PackedList<nBits>::iteratorBase::iteratorBase
(
const iteratorBase& iter
)
:
list_(iter.list_),
index_(iter.index_),
offset_(iter.offset_)
index_(0)
{}
......@@ -157,8 +126,7 @@ inline Foam::PackedList<nBits>::iteratorBase::iteratorBase
)
:
list_(const_cast<PackedList<nBits>*>(lst)),
index_(i / packing()),
offset_(i % packing())
index_(i)
{}
......@@ -166,8 +134,11 @@ template<unsigned nBits>
inline unsigned int
Foam::PackedList<nBits>::iteratorBase::get() const
{
const unsigned int& stored = list_->StorageList::operator[](index_);
return (stored >> (nBits * offset_)) & max_value();
const unsigned int seg = index_ / packing();
const unsigned int off = index_ % packing();
const unsigned int& stored = list_->StorageList::operator[](seg);
return (stored >> (nBits * off)) & max_value();
}
......@@ -175,114 +146,37 @@ template<unsigned nBits>
inline bool
Foam::PackedList<nBits>::iteratorBase::set(const unsigned int val)
{
unsigned int& stored = list_->StorageList::operator[](index_);
const unsigned int seg = index_ / packing();
const unsigned int off = index_ % packing();
unsigned int& stored = list_->StorageList::operator[](seg);
const unsigned int prev = stored;
const unsigned int startBit = nBits * offset_;
const unsigned int startBit = nBits * off;
const unsigned int maskNew = max_value() << startBit;
if (val & ~max_value())
{
# ifdef DEBUGList
FatalErrorIn("PackedList<T>::iteratorBase::set(const unsigned int)")
<< "value " << label(val)
w << " out-of-range 0 ... " << label(max_value())
<< " representable by " << nBits << " bits"
<< abort(FatalError);
# endif
// treat overflow as max_value
// overflow is max_value, fill everything
stored |= maskNew;
}
else
{
stored = (stored & ~maskNew) | (maskNew & (val << startBit));
stored &= ~maskNew;
stored |= maskNew & (val << startBit);
}
return prev != stored;
}
template<unsigned nBits>
inline void
Foam::PackedList<nBits>::iteratorBase::incr()
{
offset_++;
if (offset_ >= packing())
{
offset_ = 0;
index_++;
}
}
template<unsigned nBits>
inline void
Foam::PackedList<nBits>::iteratorBase::decr()
{
if (!offset_)
{
offset_ = packing();
index_--;
}
offset_--;
}
template<unsigned nBits>
inline void
Foam::PackedList<nBits>::iteratorBase::seek
(
const iteratorBase& iter
)
{
list_ = iter.list_;
index_ = iter.index_;
offset_ = iter.offset_;
this->validate();
}
template<unsigned nBits>
inline bool
Foam::PackedList<nBits>::iteratorBase::valid() const
{
label elemI = offset_ + index_ * packing();
return (elemI < list_->size_);
}
template<unsigned nBits>
inline bool
Foam::PackedList<nBits>::iteratorBase::validate()
{
// avoid going past end()
unsigned endIdx = list_->size_ / packing();