WIP: tl::BitSet plus tests

This commit is contained in:
Matthias Koefferlein 2024-08-25 18:20:55 +02:00
parent a8bedf7116
commit 100d861336
9 changed files with 654 additions and 0 deletions

View File

@ -11,6 +11,9 @@ FORMS =
SOURCES = \
tlAssert.cc \
tlBase64.cc \
tlBitSet.cc \
tlBitSetMap.cc \
tlBitSetMatch.cc \
tlColor.cc \
tlClassRegistry.cc \
tlCopyOnWrite.cc \
@ -62,6 +65,9 @@ HEADERS = \
tlAlgorithm.h \
tlAssert.h \
tlBase64.h \
tlBitSet.h \
tlBitSetMap.h \
tlBitSetMatch.h \
tlColor.h \
tlClassRegistry.h \
tlCopyOnWrite.h \

199
src/tl/tl/tlBitSet.cc Normal file
View File

@ -0,0 +1,199 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2024 Matthias Koefferlein
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 2 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, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "tlCommon.h"
#include "tlBitSet.h"
namespace tl
{
static inline unsigned int nwords (BitSet::size_type size)
{
return (size + (sizeof (BitSet::data_type) * 8 - 1)) / (sizeof (BitSet::data_type) * 8);
}
static inline unsigned int word (BitSet::size_type index)
{
return index / (sizeof (BitSet::data_type) * 8);
}
static inline unsigned int bit (BitSet::size_type index)
{
// first bit is the highest bit, so that comparing the uint's is good enough
// for lexical order.
return 31 - (index % (sizeof (BitSet::data_type) * 8));
}
BitSet &
BitSet::operator= (const BitSet &other)
{
if (&other != this) {
clear ();
// reallocate
m_size = other.m_size;
unsigned int words = nwords (m_size);
mp_data = new data_type[words];
data_type *t = mp_data;
data_type *s = other.mp_data;
for (unsigned int i = 0; i < words; ++i) {
*t++ = *s++;
}
}
return *this;
}
BitSet &
BitSet::operator= (BitSet &&other)
{
if (&other != this) {
swap (other);
}
return *this;
}
void
BitSet::clear ()
{
if (mp_data) {
delete [] mp_data;
}
mp_data = 0;
m_size = 0;
}
void
BitSet::resize (size_type size)
{
if (size > m_size) {
unsigned int words = nwords (m_size);
unsigned int new_words = nwords (size);
if (new_words > words) {
// reallocate
data_type *new_data = new data_type[new_words];
data_type *t = new_data;
data_type *s = mp_data;
unsigned int i;
for (i = 0; i < words; ++i) {
*t++ = *s++;
}
for (; i < new_words; ++i) {
*t++ = 0;
}
delete mp_data;
mp_data = new_data;
m_size = size;
}
}
}
bool
BitSet::operator== (const BitSet &other) const
{
unsigned int words = nwords (m_size);
unsigned int other_words = nwords (other.m_size);
const data_type *p = mp_data;
const data_type *op = other.mp_data;
unsigned int i;
for (i = 0; i < words && i < other_words; ++i) {
if (*p++ != *op++) {
return false;
}
}
for (; i < words; ++i) {
if (*p++ != 0) {
return false;
}
}
for (; i < other_words; ++i) {
if (0 != *op++) {
return false;
}
}
return true;
}
bool
BitSet::operator< (const BitSet &other) const
{
unsigned int words = nwords (m_size);
unsigned int other_words = nwords (other.m_size);
const data_type *p = mp_data;
const data_type *op = other.mp_data;
unsigned int i;
for (i = 0; i < words && i < other_words; ++i, ++p, ++op) {
if (*p != *op) {
return *p < *op;
}
}
for (; i < other_words; ++i, ++op) {
if (0 != *op) {
return true;
}
}
return false;
}
void
BitSet::set (index_type index)
{
unsigned int wi = word (index);
if (wi >= nwords (m_size)) {
resize (index + 1);
} else if (index >= m_size) {
m_size = index + 1;
}
mp_data [wi] |= (1 << bit (index));
}
void
BitSet::reset (index_type index)
{
if (index < m_size) {
unsigned int wi = word (index);
mp_data [wi] &= ~(1 << bit (index));
}
}
bool
BitSet::operator[] (index_type index) const
{
if (index < m_size) {
unsigned int wi = word (index);
return (mp_data [wi] & (1 << bit (index))) != 0;
} else {
return false;
}
}
}

240
src/tl/tl/tlBitSet.h Normal file
View File

@ -0,0 +1,240 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2024 Matthias Koefferlein
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 2 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, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef HDR_tlBitSet
#define HDR_tlBitSet
#include "tlCommon.h"
#include <cstdint>
#include <algorithm>
namespace tl
{
/**
* @brief A bit set
*
* This object can store a set of n bits, each being true or false.
* Essentially is it like a vector<bool>, but optimized to cooperate
* with tl::BitSetMap and tl::BitSetMatch.
*
* Allocation is dynamic when a bit is accessed for write. Bits beyond the
* size are treated as false.
*/
class TL_PUBLIC BitSet
{
public:
typedef unsigned int index_type;
typedef unsigned int size_type;
typedef uint32_t data_type;
/**
* @brief Default constructor: creates an empty bit set
*/
BitSet ()
: mp_data (0), m_size (0)
{
// .. nothing yet ..
}
/**
* @brief Creates and initializes a bit set from a range of indexes
* Every bit given by an index from the range is set.
*/
template <class Iter>
BitSet (Iter from, Iter to)
: mp_data (0), m_size (0)
{
set (from, to);
}
/**
* @brief Copy constructor
*/
BitSet (const BitSet &other)
: mp_data (0), m_size (0)
{
operator= (other);
}
/**
* @brief Move constructor
*/
BitSet (BitSet &&other)
: mp_data (0), m_size (0)
{
operator= (std::move (other));
}
/**
* @brief Destructor
*/
~BitSet ()
{
clear ();
}
/**
* @brief Assignment
*/
BitSet &operator= (const BitSet &other);
/**
* @brief Move assignment
*/
BitSet &operator= (BitSet &&other);
/**
* @brief Swaps the contents of this bit set with the other
*/
void swap (BitSet &other)
{
std::swap (mp_data, other.mp_data);
std::swap (m_size, other.m_size);
}
/**
* @brief Clears this bit set
*/
void clear ();
/**
* @brief Sizes the bit set to "size" bits
*
* New bits are set to false.
*/
void resize (size_type size);
/**
* @brief Equality
*/
bool operator== (const BitSet &other) const;
/**
* @brief Inequality
*/
bool operator!= (const BitSet &other) const
{
return !operator== (other);
}
/**
* @brief Less operator
*
* The bits are compared in lexical order, first bit first.
*/
bool operator< (const BitSet &other) const;
/**
* @brief Sets the given bit
*/
void set (index_type index);
/**
* @brief Sets a range of bits
* The indexes are taken from the sequence delivered by the iterator.
*/
template <class Iter>
void set (Iter from, Iter to)
{
for (Iter i = from; i != to; ++i) {
set (*i);
}
}
/**
* @brief Resets the given bit
*/
void reset (index_type index);
/**
* @brief Resets a range of bits
* The indexes are taken from the sequence delivered by the iterator.
*/
template <class Iter>
void reset (Iter from, Iter to)
{
for (Iter i = from; i != to; ++i) {
reset (*i);
}
}
/**
* @brief Sets the values for a given bit
*/
void set_value (index_type index, bool f)
{
if (f) {
set (index);
} else {
reset (index);
}
}
/**
* @brief Sets the values for a range of bits
* The indexes are taken from the sequence delivered by the iterator.
*/
template <class Iter>
void set_value (Iter from, Iter to, bool f)
{
for (Iter i = from; i != to; ++i) {
set_value (*i, f);
}
}
/**
* @brief Gets a bit from the given index
*/
bool operator[] (index_type index) const;
/**
* @brief Gets a value indicating whether the set is empty
*
* "empty" means, no bits have been written yet. "empty" does NOT mean
* all bits are zero.
*/
bool is_empty () const
{
return m_size == 0;
}
/**
* @brief Gets the number of bits stored
*
* The number of bits is the highest bit written so far.
*/
size_type size () const
{
return m_size;
}
private:
data_type *mp_data;
size_type m_size;
};
}
#endif

0
src/tl/tl/tlBitSetMap.cc Normal file
View File

0
src/tl/tl/tlBitSetMap.h Normal file
View File

View File

View File

View File

@ -0,0 +1,208 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2024 Matthias Koefferlein
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 2 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, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "tlBitSet.h"
#include "tlUnitTest.h"
#include "tlString.h"
namespace
{
static std::string l2s (const tl::BitSet &s)
{
std::string x;
for (tl::BitSet::index_type i = 0; i < s.size (); ++i) {
x += s[i] ? "1" : "0";
}
return x;
}
TEST(1_Basic)
{
tl::BitSet bs;
EXPECT_EQ (bs.is_empty (), true);
EXPECT_EQ (bs.size (), 0u);
EXPECT_EQ (l2s (bs), "");
bs.set (1);
EXPECT_EQ (bs.size (), 2u);
EXPECT_EQ (l2s (bs), "01");
bs.set (32);
EXPECT_EQ (bs.size (), 33u);
EXPECT_EQ (l2s (bs), "010000000000000000000000000000001");
bs.set (3);
EXPECT_EQ (bs.size (), 33u);
EXPECT_EQ (l2s (bs), "010100000000000000000000000000001");
unsigned int indexes[] = { 5, 6, 7 };
bs.set (indexes + 0, indexes + sizeof (indexes) / sizeof (indexes [0]));
EXPECT_EQ (bs.size (), 33u);
EXPECT_EQ (l2s (bs), "010101110000000000000000000000001");
bs.reset (128);
EXPECT_EQ (bs.size (), 33u);
EXPECT_EQ (l2s (bs), "010101110000000000000000000000001");
bs.reset (1);
EXPECT_EQ (bs.size (), 33u);
EXPECT_EQ (l2s (bs), "000101110000000000000000000000001");
bs.reset (indexes + 0, indexes + sizeof (indexes) / sizeof (indexes [0]));
EXPECT_EQ (bs.size (), 33u);
EXPECT_EQ (l2s (bs), "000100000000000000000000000000001");
bs.set_value (0, true);
EXPECT_EQ (bs.size (), 33u);
EXPECT_EQ (l2s (bs), "100100000000000000000000000000001");
bs.set_value (indexes + 0, indexes + sizeof (indexes) / sizeof (indexes [0]), true);
EXPECT_EQ (bs.size (), 33u);
EXPECT_EQ (l2s (bs), "100101110000000000000000000000001");
bs.set_value (indexes + 0, indexes + sizeof (indexes) / sizeof (indexes [0]), false);
EXPECT_EQ (bs.size (), 33u);
EXPECT_EQ (l2s (bs), "100100000000000000000000000000001");
bs.set_value (0, false);
EXPECT_EQ (bs.size (), 33u);
EXPECT_EQ (l2s (bs), "000100000000000000000000000000001");
bs.clear ();
EXPECT_EQ (bs.size (), 0u);
EXPECT_EQ (l2s (bs), "");
bs.resize (6);
EXPECT_EQ (bs.size (), 6u);
EXPECT_EQ (l2s (bs), "000000");
}
TEST(2_Equality)
{
tl::BitSet bs1, bs2, bs3;
EXPECT_EQ (bs1 == bs2, true);
EXPECT_EQ (bs1 != bs2, false);
bs1.set (0);
EXPECT_EQ (bs1 == bs2, false);
EXPECT_EQ (bs1 != bs2, true);
bs1.set (32);
EXPECT_EQ (bs1 == bs2, false);
EXPECT_EQ (bs1 != bs2, true);
bs2.set (0);
bs2.set (32);
EXPECT_EQ (bs1 == bs2, true);
EXPECT_EQ (bs1 == bs3, false);
EXPECT_EQ (bs1 != bs2, false);
EXPECT_EQ (bs1 != bs3, true);
bs1.reset (0);
bs1.reset (32);
EXPECT_EQ (bs1 == bs2, false);
EXPECT_EQ (bs1 == bs3, true);
EXPECT_EQ (bs1 != bs2, true);
EXPECT_EQ (bs1 != bs3, false);
}
TEST(3_Compare)
{
tl::BitSet bs1, bs2, bs3;
EXPECT_EQ (bs1 < bs2, false);
EXPECT_EQ (bs2 < bs1, false);
bs1.set (0);
EXPECT_EQ (bs1 < bs2, false);
EXPECT_EQ (bs2 < bs1, true);
bs1.set (32);
EXPECT_EQ (bs1 < bs2, false);
EXPECT_EQ (bs2 < bs1, true);
bs2.set (0);
bs2.set (32);
EXPECT_EQ (bs1 < bs2, false);
EXPECT_EQ (bs1 < bs3, false);
EXPECT_EQ (bs2 < bs1, false);
EXPECT_EQ (bs3 < bs1, true);
bs1.reset (0);
bs1.reset (32);
EXPECT_EQ (bs1 < bs2, true);
EXPECT_EQ (bs1 < bs3, false);
EXPECT_EQ (bs2 < bs1, false);
EXPECT_EQ (bs3 < bs1, false);
}
TEST(4_Assign)
{
tl::BitSet bs;
EXPECT_EQ (l2s (bs), "");
EXPECT_EQ (l2s (tl::BitSet (bs)), "");
bs.set (3);
bs.set (32);
EXPECT_EQ (bs.size (), 33u);
EXPECT_EQ (l2s (bs), "000100000000000000000000000000001");
EXPECT_EQ (tl::BitSet (bs).size (), 33u);
EXPECT_EQ (l2s (tl::BitSet (bs)), "000100000000000000000000000000001");
tl::BitSet bs2;
bs2.swap (bs);
EXPECT_EQ (bs.size (), 0u);
EXPECT_EQ (bs2.size (), 33u);
EXPECT_EQ (l2s (bs), "");
EXPECT_EQ (l2s (bs2), "000100000000000000000000000000001");
bs = bs2;
EXPECT_EQ (bs.size (), 33u);
EXPECT_EQ (l2s (bs), "000100000000000000000000000000001");
bs2.clear ();
EXPECT_EQ (bs2.size (), 0u);
EXPECT_EQ (l2s (bs2), "");
bs2 = std::move (bs);
EXPECT_EQ (bs.size (), 0u);
EXPECT_EQ (l2s (bs), "");
EXPECT_EQ (bs2.size (), 33u);
EXPECT_EQ (l2s (bs2), "000100000000000000000000000000001");
tl::BitSet bs3 (std::move (bs2));
EXPECT_EQ (bs2.size (), 0u);
EXPECT_EQ (l2s (bs2), "");
EXPECT_EQ (bs3.size (), 33u);
EXPECT_EQ (l2s (bs3), "000100000000000000000000000000001");
bs.clear ();
unsigned int indexes[] = { 5, 6, 7 };
bs = std::move (tl::BitSet (indexes + 0, indexes + sizeof (indexes) / sizeof (indexes [0])));
EXPECT_EQ (bs.size (), 8u);
EXPECT_EQ (l2s (bs), "00000111");
}
}

View File

@ -9,6 +9,7 @@ include($$PWD/../../lib_ut.pri)
SOURCES = \
tlAlgorithmTests.cc \
tlBase64Tests.cc \
tlBitSetTests.cc \
tlClassRegistryTests.cc \
tlCommandLineParserTests.cc \
tlColorTests.cc \