WIP: tl::BitSetMask

This commit is contained in:
Matthias Koefferlein 2024-08-25 20:26:48 +02:00
parent 100d861336
commit 7219742b87
8 changed files with 749 additions and 2 deletions

View File

@ -13,7 +13,7 @@ SOURCES = \
tlBase64.cc \
tlBitSet.cc \
tlBitSetMap.cc \
tlBitSetMatch.cc \
tlBitSetMask.cc \
tlColor.cc \
tlClassRegistry.cc \
tlCopyOnWrite.cc \
@ -67,7 +67,7 @@ HEADERS = \
tlBase64.h \
tlBitSet.h \
tlBitSetMap.h \
tlBitSetMatch.h \
tlBitSetMask.h \
tlColor.h \
tlClassRegistry.h \
tlCopyOnWrite.h \

View File

@ -231,6 +231,8 @@ public:
}
private:
friend class BitSetMask;
data_type *mp_data;
size_type m_size;
};

291
src/tl/tl/tlBitSetMask.cc Normal file
View File

@ -0,0 +1,291 @@
/*
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 "tlBitSetMask.h"
#include "tlBitSet.h"
namespace tl
{
static inline unsigned int nwords (BitSetMask::size_type size)
{
return (size + (sizeof (BitSetMask::data_type) * 8 - 1)) / (sizeof (BitSetMask::data_type) * 8);
}
static inline unsigned int word (BitSetMask::size_type index)
{
return index / (sizeof (BitSetMask::data_type) * 8);
}
static inline unsigned int bit (BitSetMask::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 (BitSetMask::data_type) * 8));
}
BitSetMask &
BitSetMask::operator= (const BitSetMask &other)
{
if (&other != this) {
clear ();
// reallocate
m_size = other.m_size;
unsigned int words = nwords (m_size);
mp_data0 = new data_type[words];
mp_data1 = new data_type[words];
data_type *t0 = mp_data0;
data_type *s0 = other.mp_data0;
data_type *t1 = mp_data1;
data_type *s1 = other.mp_data1;
for (unsigned int i = 0; i < words; ++i) {
*t0++ = *s0++;
*t1++ = *s1++;
}
}
return *this;
}
BitSetMask &
BitSetMask::operator= (BitSetMask &&other)
{
if (&other != this) {
swap (other);
}
return *this;
}
void
BitSetMask::clear ()
{
if (mp_data0) {
delete [] mp_data0;
}
mp_data0 = 0;
if (mp_data1) {
delete [] mp_data1;
}
mp_data1 = 0;
m_size = 0;
}
void
BitSetMask::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_data0 = new data_type[new_words];
data_type *new_data1 = new data_type[new_words];
data_type *t0 = new_data0;
data_type *s0 = mp_data0;
data_type *t1 = new_data1;
data_type *s1 = mp_data1;
unsigned int i;
for (i = 0; i < words; ++i) {
*t0++ = *s0++;
*t1++ = *s1++;
}
for (; i < new_words; ++i) {
// corresponds to "Any"
*t0++ = 0;
*t1++ = 0;
}
delete mp_data0;
mp_data0 = new_data0;
delete mp_data1;
mp_data1 = new_data1;
m_size = size;
}
}
}
bool
BitSetMask::operator== (const BitSetMask &other) const
{
unsigned int words = nwords (m_size);
unsigned int other_words = nwords (other.m_size);
const data_type *p0 = mp_data0;
const data_type *p1 = mp_data1;
const data_type *op0 = other.mp_data0;
const data_type *op1 = other.mp_data1;
unsigned int i;
for (i = 0; i < words && i < other_words; ++i) {
if (*p0++ != *op0++) {
return false;
}
if (*p1++ != *op1++) {
return false;
}
}
for (; i < words; ++i) {
if (*p0++ != 0) {
return false;
}
if (*p1++ != 0) {
return false;
}
}
for (; i < other_words; ++i) {
if (0 != *op0++) {
return false;
}
if (0 != *op1++) {
return false;
}
}
return true;
}
/**
* @brief Gets the most significant bit of a bit set
*
* For example b:00101101 will give b:00100000.
*/
static inline BitSetMask::data_type msb_only (BitSetMask::data_type value)
{
const unsigned int smax = sizeof (BitSetMask::data_type) * 8;
BitSetMask::data_type m = value;
for (unsigned int s = 1; s < smax; s *= 2) {
m |= (m >> s);
}
return value & ~(m >> 1);
}
bool
BitSetMask::operator< (const BitSetMask &other) const
{
unsigned int words = nwords (m_size);
unsigned int other_words = nwords (other.m_size);
const data_type *p0 = mp_data0;
const data_type *p1 = mp_data1;
const data_type *op0 = other.mp_data0;
const data_type *op1 = other.mp_data1;
unsigned int i;
for (i = 0; i < words && i < other_words; ++i, ++p0, ++p1, ++op0, ++op1) {
data_type diff = (*p0 ^ *op0) | (*p1 ^ *op1);
if (diff) {
// compare the most significant position of the differences by value
data_type mb = msb_only (diff);
unsigned int m = ((*p0 & mb) != 0 ? 1 : 0) + ((*p1 & mb) != 0 ? 2 : 0);
unsigned int om = ((*op0 & mb) != 0 ? 1 : 0) + ((*op1 & mb) != 0 ? 2 : 0);
return m < om;
}
}
// the remaining part of other is simply checked for
// not being zero
for (; i < other_words; ++i, ++op0, ++op1) {
if (0 != *op0 || 0 != *op1) {
return true;
}
}
return false;
}
void
BitSetMask::set (index_type index, mask_type mask)
{
if (index >= m_size && mask == Any) {
return;
}
unsigned int wi = word (index);
if (wi >= nwords (m_size)) {
resize (index + 1);
} else if (index >= m_size) {
m_size = index + 1;
}
unsigned int mi = (unsigned int) mask;
data_type bm = (1 << bit (index));
if (mi & 1) {
mp_data0 [wi] |= bm;
} else {
mp_data0 [wi] &= ~bm;
}
if (mi & 2) {
mp_data1 [wi] |= bm;
} else {
mp_data1 [wi] &= ~bm;
}
}
BitSetMask::mask_type
BitSetMask::operator[] (index_type index) const
{
if (index < m_size) {
unsigned int wi = word (index);
data_type bm = (1 << bit (index));
unsigned int mi = ((mp_data0 [wi] & bm) != 0 ? 1 : 0) | ((mp_data1 [wi] & bm) != 0 ? 2 : 0);
return mask_type (mi);
} else {
return Any;
}
}
bool
BitSetMask::match (const tl::BitSet &bs) const
{
unsigned nw_bs = nwords (bs.m_size);
unsigned nw = nwords (m_size);
const tl::BitSet::data_type *d0 = mp_data0, *d1 = mp_data1;
const tl::BitSet::data_type *s = bs.mp_data;
unsigned int i;
for (i = 0; i < nw_bs && i < nw; ++i, ++d0, ++d1, ++s) {
tl::BitSet::data_type d = *s;
// A "true" in place of "false expected" gives "no match"
if ((*d0 & d) != 0) {
return false;
}
// A "false" in place of "true expected" gives "no match"
if ((*d1 & ~d) != 0) {
return false;
}
}
// as "not set" corresponds to "Any", we can stop here and have a match.
return true;
}
}

189
src/tl/tl/tlBitSetMask.h Normal file
View File

@ -0,0 +1,189 @@
/*
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_tlBitSetMask
#define HDR_tlBitSetMask
#include "tlCommon.h"
#include "tlBitSet.h"
#include <cstdint>
#include <algorithm>
namespace tl
{
/**
* @brief A bit set
*
* This object can store a mask for a bit set.
* Each element of the mask corresponds to one bit. Each element can be "True"
* (matching to true), "False" (matching to false), "Any" matching to true
* or false and "Never" matches neither to true nor false.
*
* A bit set match can be matched against a bit set and will return true if
* the bit set corresponds to the mask.
*
* Allocation is dynamic when a mask element is accessed for write. Bits beyond the
* size are treated as "Any".
*/
class TL_PUBLIC BitSetMask
{
public:
typedef enum { Any = 0, False = 1, True = 2, Never = 3 } mask_type;
typedef tl::BitSet::index_type index_type;
typedef tl::BitSet::size_type size_type;
typedef tl::BitSet::data_type data_type;
/**
* @brief Default constructor: creates an empty bit set
*/
BitSetMask ()
: mp_data0 (0), mp_data1 (0), m_size (0)
{
// .. nothing yet ..
}
/**
* @brief Copy constructor
*/
BitSetMask (const BitSetMask &other)
: mp_data0 (0), mp_data1 (0), m_size (0)
{
operator= (other);
}
/**
* @brief Move constructor
*/
BitSetMask (BitSetMask &&other)
: mp_data0 (0), mp_data1 (0), m_size (0)
{
operator= (std::move (other));
}
/**
* @brief Destructor
*/
~BitSetMask ()
{
clear ();
}
/**
* @brief Assignment
*/
BitSetMask &operator= (const BitSetMask &other);
/**
* @brief Move assignment
*/
BitSetMask &operator= (BitSetMask &&other);
/**
* @brief Swaps the contents of this bit set with the other
*/
void swap (BitSetMask &other)
{
std::swap (mp_data0, other.mp_data0);
std::swap (mp_data1, other.mp_data1);
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 BitSetMask &other) const;
/**
* @brief Inequality
*/
bool operator!= (const BitSetMask &other) const
{
return !operator== (other);
}
/**
* @brief Less operator
*
* The bits are compared in lexical order, first bit first.
*/
bool operator< (const BitSetMask &other) const;
/**
* @brief Sets the mask for the given bit
*/
void set (index_type index, mask_type mask);
/**
* @brief Gets a mask from the given bit
*/
mask_type 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 masks are of some specific value.
*/
bool is_empty () const
{
return m_size == 0;
}
/**
* @brief Gets the number of bits for the mask stored
*
* The number of bits is the highest bit written so far.
*/
size_type size () const
{
return m_size;
}
/**
* @brief Matches the given bit set against this mask
*/
bool match (const tl::BitSet &) const;
private:
data_type *mp_data0, *mp_data1;
size_type m_size;
};
}
#endif

View File

@ -0,0 +1,264 @@
/*
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 "tlBitSetMask.h"
#include "tlUnitTest.h"
#include "tlString.h"
namespace
{
static std::string l2s (const tl::BitSetMask &s)
{
std::string x;
for (tl::BitSetMask::index_type i = 0; i < s.size (); ++i) {
switch (s[i]) {
case tl::BitSetMask::Any:
x += "X";
break;
case tl::BitSetMask::True:
x += "1";
break;
case tl::BitSetMask::False:
x += "0";
break;
default:
x += "-";
break;
}
}
return x;
}
TEST(1_Basic)
{
tl::BitSetMask bs;
EXPECT_EQ (bs.is_empty (), true);
EXPECT_EQ (bs.size (), 0u);
EXPECT_EQ (l2s (bs), "");
bs.set (1, tl::BitSetMask::True);
EXPECT_EQ (bs.size (), 2u);
EXPECT_EQ (l2s (bs), "X1");
bs.set (32, tl::BitSetMask::False);
EXPECT_EQ (bs.size (), 33u);
EXPECT_EQ (l2s (bs), "X1XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX0");
bs.set (3, tl::BitSetMask::False);
EXPECT_EQ (bs.size (), 33u);
EXPECT_EQ (l2s (bs), "X1X0XXXXXXXXXXXXXXXXXXXXXXXXXXXX0");
bs.set (128, tl::BitSetMask::Any);
EXPECT_EQ (bs.size (), 33u);
EXPECT_EQ (l2s (bs), "X1X0XXXXXXXXXXXXXXXXXXXXXXXXXXXX0");
bs.clear ();
EXPECT_EQ (bs.size (), 0u);
EXPECT_EQ (l2s (bs), "");
bs.resize (6);
EXPECT_EQ (bs.size (), 6u);
EXPECT_EQ (l2s (bs), "XXXXXX");
}
TEST(2_Equality)
{
tl::BitSetMask bs1, bs2, bs3;
EXPECT_EQ (bs1 == bs2, true);
EXPECT_EQ (bs1 != bs2, false);
bs1.set (0, tl::BitSetMask::True);
EXPECT_EQ (bs1 == bs2, false);
EXPECT_EQ (bs1 != bs2, true);
bs1.set (32, tl::BitSetMask::False);
EXPECT_EQ (bs1 == bs2, false);
EXPECT_EQ (bs1 != bs2, true);
bs2.set (0, tl::BitSetMask::True);
bs2.set (32, tl::BitSetMask::False);
EXPECT_EQ (bs1 == bs2, true);
EXPECT_EQ (bs1 == bs3, false);
EXPECT_EQ (bs1 != bs2, false);
EXPECT_EQ (bs1 != bs3, true);
bs1.set (0, tl::BitSetMask::Any);
bs1.set (32, tl::BitSetMask::Any);
EXPECT_EQ (bs1 == bs2, false);
EXPECT_EQ (bs1 == bs3, true);
EXPECT_EQ (bs1 != bs2, true);
EXPECT_EQ (bs1 != bs3, false);
}
TEST(3_Compare)
{
tl::BitSetMask bs1, bs2, bs3;
EXPECT_EQ (bs1 < bs2, false);
EXPECT_EQ (bs2 < bs1, false);
bs1.set (0, tl::BitSetMask::True);
EXPECT_EQ (bs1 < bs2, false);
EXPECT_EQ (bs2 < bs1, true);
bs1.set (32, tl::BitSetMask::False);
EXPECT_EQ (bs1 < bs2, false);
EXPECT_EQ (bs2 < bs1, true);
bs2.set (32, tl::BitSetMask::False);
EXPECT_EQ (bs1 < bs2, false);
EXPECT_EQ (bs1 < bs3, false);
EXPECT_EQ (bs2 < bs1, true);
EXPECT_EQ (bs3 < bs1, true);
bs2.set (0, tl::BitSetMask::True);
EXPECT_EQ (bs1 < bs2, false);
EXPECT_EQ (bs1 < bs3, false);
EXPECT_EQ (bs2 < bs1, false);
EXPECT_EQ (bs3 < bs1, true);
bs1.set (0, tl::BitSetMask::Any);
bs1.set (32, tl::BitSetMask::Any);
EXPECT_EQ (bs1 < bs2, true);
EXPECT_EQ (bs1 < bs3, false);
EXPECT_EQ (bs2 < bs1, false);
EXPECT_EQ (bs3 < bs1, false);
bs1.clear ();
bs2.clear ();
bs1.set (0, tl::BitSetMask::Any);
bs2.set (0, tl::BitSetMask::Any);
EXPECT_EQ (bs1 < bs2, false);
EXPECT_EQ (bs2 < bs1, false);
bs2.set (0, tl::BitSetMask::False);
EXPECT_EQ (bs1 < bs2, true);
EXPECT_EQ (bs2 < bs1, false);
bs2.set (0, tl::BitSetMask::True);
EXPECT_EQ (bs1 < bs2, true);
EXPECT_EQ (bs2 < bs1, false);
bs2.set (0, tl::BitSetMask::Never);
EXPECT_EQ (bs1 < bs2, true);
EXPECT_EQ (bs2 < bs1, false);
bs1.set (0, tl::BitSetMask::False);
bs2.set (0, tl::BitSetMask::Any);
EXPECT_EQ (bs1 < bs2, false);
EXPECT_EQ (bs2 < bs1, true);
bs2.set (0, tl::BitSetMask::False);
EXPECT_EQ (bs1 < bs2, false);
EXPECT_EQ (bs2 < bs1, false);
bs2.set (0, tl::BitSetMask::True);
EXPECT_EQ (bs1 < bs2, true);
EXPECT_EQ (bs2 < bs1, false);
bs2.set (0, tl::BitSetMask::Never);
EXPECT_EQ (bs1 < bs2, true);
EXPECT_EQ (bs2 < bs1, false);
bs1.set (0, tl::BitSetMask::True);
bs2.set (0, tl::BitSetMask::Any);
EXPECT_EQ (bs1 < bs2, false);
EXPECT_EQ (bs2 < bs1, true);
bs2.set (0, tl::BitSetMask::False);
EXPECT_EQ (bs1 < bs2, false);
EXPECT_EQ (bs2 < bs1, true);
bs2.set (0, tl::BitSetMask::True);
EXPECT_EQ (bs1 < bs2, false);
EXPECT_EQ (bs2 < bs1, false);
bs2.set (0, tl::BitSetMask::Never);
EXPECT_EQ (bs1 < bs2, true);
EXPECT_EQ (bs2 < bs1, false);
bs1.set (0, tl::BitSetMask::Never);
bs2.set (0, tl::BitSetMask::Any);
EXPECT_EQ (bs1 < bs2, false);
EXPECT_EQ (bs2 < bs1, true);
bs2.set (0, tl::BitSetMask::False);
EXPECT_EQ (bs1 < bs2, false);
EXPECT_EQ (bs2 < bs1, true);
bs2.set (0, tl::BitSetMask::True);
EXPECT_EQ (bs1 < bs2, false);
EXPECT_EQ (bs2 < bs1, true);
bs2.set (0, tl::BitSetMask::Never);
EXPECT_EQ (bs1 < bs2, false);
EXPECT_EQ (bs2 < bs1, false);
}
TEST(4_Assign)
{
tl::BitSetMask bs;
EXPECT_EQ (l2s (bs), "");
EXPECT_EQ (l2s (tl::BitSetMask (bs)), "");
bs.set (3, tl::BitSetMask::True);
bs.set (32, tl::BitSetMask::False);
EXPECT_EQ (bs.size (), 33u);
EXPECT_EQ (l2s (bs), "XXX1XXXXXXXXXXXXXXXXXXXXXXXXXXXX0");
EXPECT_EQ (tl::BitSetMask (bs).size (), 33u);
EXPECT_EQ (l2s (tl::BitSetMask (bs)), "XXX1XXXXXXXXXXXXXXXXXXXXXXXXXXXX0");
tl::BitSetMask bs2;
bs2.swap (bs);
EXPECT_EQ (bs.size (), 0u);
EXPECT_EQ (bs2.size (), 33u);
EXPECT_EQ (l2s (bs), "");
EXPECT_EQ (l2s (bs2), "XXX1XXXXXXXXXXXXXXXXXXXXXXXXXXXX0");
bs = bs2;
EXPECT_EQ (bs.size (), 33u);
EXPECT_EQ (l2s (bs), "XXX1XXXXXXXXXXXXXXXXXXXXXXXXXXXX0");
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), "XXX1XXXXXXXXXXXXXXXXXXXXXXXXXXXX0");
tl::BitSetMask bs3 (std::move (bs2));
EXPECT_EQ (bs2.size (), 0u);
EXPECT_EQ (l2s (bs2), "");
EXPECT_EQ (bs3.size (), 33u);
EXPECT_EQ (l2s (bs3), "XXX1XXXXXXXXXXXXXXXXXXXXXXXXXXXX0");
}
}

View File

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