kernel/util: Implement more features in the Bitmap class.
* Resize(): adds more space to the end of the bitmap. * Shift(): moves all bits in the map up or down. * Use size_t instead of int for indexes. Also add unit tests for the new functions (they seem to be passing.) Reference material for shift implementation: https://github.com/ecsv/linux-like-bitops/blob/2c56d43c1ebc9fc0dddc0149a374d096198372a9/bitops.h#L977 Change-Id: Ia85768aaeed7bd3ffef3a9f575f05331e048fe50 Reviewed-on: https://review.haiku-os.org/c/haiku/+/5146 Reviewed-by: waddlesplash <[email protected]>
This commit is contained in:
committed by
waddlesplash
parent
9dacea2ee7
commit
88275138ba
@@ -1,82 +1,87 @@
|
||||
/*
|
||||
* Copyright 2013 Haiku, Inc. All rights reserved.
|
||||
* Copyright 2013-2022, Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, [email protected]
|
||||
* Augustin Cavalier <waddlesplash>
|
||||
*/
|
||||
#ifndef KERNEL_UTIL_BITMAP_H
|
||||
#define KERNEL_UTIL_BITMAP_H
|
||||
|
||||
|
||||
#include <debug.h>
|
||||
#ifdef _KERNEL_MODE
|
||||
# include <debug.h>
|
||||
#else
|
||||
# include <Debug.h>
|
||||
#endif
|
||||
|
||||
#include <SupportDefs.h>
|
||||
|
||||
namespace BKernel {
|
||||
|
||||
class Bitmap {
|
||||
public:
|
||||
Bitmap(int bitCount);
|
||||
Bitmap(size_t bitCount);
|
||||
~Bitmap();
|
||||
|
||||
inline status_t GetInitStatus();
|
||||
status_t InitCheck();
|
||||
|
||||
inline bool Get(int index) const;
|
||||
inline void Set(int index);
|
||||
inline void Clear(int index);
|
||||
status_t Resize(size_t bitCount);
|
||||
void Shift(ssize_t bitCount);
|
||||
|
||||
int GetHighestSet() const;
|
||||
inline bool Get(size_t index) const;
|
||||
inline void Set(size_t index);
|
||||
inline void Clear(size_t index);
|
||||
|
||||
ssize_t GetHighestSet() const;
|
||||
|
||||
private:
|
||||
status_t fInitStatus;
|
||||
|
||||
int fElementsCount;
|
||||
int fSize;
|
||||
size_t fElementsCount;
|
||||
size_t fSize;
|
||||
addr_t* fBits;
|
||||
|
||||
static const int kBitsPerElement;
|
||||
static const int kBitsPerElement = (sizeof(addr_t) * 8);
|
||||
};
|
||||
|
||||
|
||||
status_t
|
||||
Bitmap::GetInitStatus()
|
||||
{
|
||||
return fInitStatus;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Bitmap::Get(int index) const
|
||||
Bitmap::Get(size_t index) const
|
||||
{
|
||||
ASSERT(index < fSize);
|
||||
|
||||
const int kArrayElement = index / kBitsPerElement;
|
||||
const size_t kArrayElement = index / kBitsPerElement;
|
||||
const addr_t kBitMask = addr_t(1) << (index % kBitsPerElement);
|
||||
return fBits[kArrayElement] & kBitMask;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Bitmap::Set(int index)
|
||||
Bitmap::Set(size_t index)
|
||||
{
|
||||
ASSERT(index < fSize);
|
||||
|
||||
const int kArrayElement = index / kBitsPerElement;
|
||||
const size_t kArrayElement = index / kBitsPerElement;
|
||||
const addr_t kBitMask = addr_t(1) << (index % kBitsPerElement);
|
||||
fBits[kArrayElement] |= kBitMask;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Bitmap::Clear(int index)
|
||||
Bitmap::Clear(size_t index)
|
||||
{
|
||||
ASSERT(index < fSize);
|
||||
|
||||
const int kArrayElement = index / kBitsPerElement;
|
||||
const size_t kArrayElement = index / kBitsPerElement;
|
||||
const addr_t kBitMask = addr_t(1) << (index % kBitsPerElement);
|
||||
fBits[kArrayElement] &= ~addr_t(kBitMask);
|
||||
}
|
||||
|
||||
} // namespace BKernel
|
||||
|
||||
|
||||
using BKernel::Bitmap;
|
||||
|
||||
|
||||
#endif // KERNEL_UTIL_BITMAP_H
|
||||
|
||||
|
||||
@@ -1,54 +1,121 @@
|
||||
/*
|
||||
* Copyright 2013 Haiku, Inc. All rights reserved.
|
||||
* Copyright 2013-2022, Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, [email protected]
|
||||
* Augustin Cavalier <waddlesplash>
|
||||
*/
|
||||
|
||||
|
||||
#include <util/Bitmap.h>
|
||||
|
||||
#include <new>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <util/BitUtils.h>
|
||||
|
||||
|
||||
const int Bitmap::kBitsPerElement = sizeof(addr_t) * 8;
|
||||
namespace BKernel {
|
||||
|
||||
|
||||
Bitmap::Bitmap(int bitCount)
|
||||
Bitmap::Bitmap(size_t bitCount)
|
||||
:
|
||||
fInitStatus(B_OK),
|
||||
fElementsCount(0),
|
||||
fSize(bitCount)
|
||||
fSize(0),
|
||||
fBits(NULL)
|
||||
{
|
||||
int count = fSize + kBitsPerElement - 1;
|
||||
count /= kBitsPerElement;
|
||||
|
||||
fBits = new(std::nothrow) addr_t[count];
|
||||
if (fBits == NULL) {
|
||||
fSize = 0;
|
||||
fInitStatus = B_NO_MEMORY;
|
||||
} else {
|
||||
fElementsCount = count;
|
||||
memset(fBits, 0, sizeof(addr_t) * count);
|
||||
}
|
||||
Resize(bitCount);
|
||||
}
|
||||
|
||||
|
||||
Bitmap::~Bitmap()
|
||||
{
|
||||
delete[] fBits;
|
||||
free(fBits);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
status_t
|
||||
Bitmap::InitCheck()
|
||||
{
|
||||
return (fBits != NULL) ? B_OK : B_NO_MEMORY;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Bitmap::Resize(size_t bitCount)
|
||||
{
|
||||
const size_t count = (bitCount + kBitsPerElement - 1) / kBitsPerElement;
|
||||
if (count == fElementsCount) {
|
||||
fSize = bitCount;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
void* bits = realloc(fBits, sizeof(addr_t) * count);
|
||||
if (bits == NULL)
|
||||
return B_NO_MEMORY;
|
||||
fBits = (addr_t*)bits;
|
||||
|
||||
if (fElementsCount < count)
|
||||
memset(&fBits[fElementsCount], 0, sizeof(addr_t) * (count - fElementsCount));
|
||||
|
||||
fSize = bitCount;
|
||||
fElementsCount = count;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Bitmap::Shift(ssize_t bitCount)
|
||||
{
|
||||
if (bitCount == 0)
|
||||
return;
|
||||
|
||||
const size_t shift = (bitCount > 0) ? bitCount : -bitCount;
|
||||
const size_t nElements = shift / kBitsPerElement, nBits = shift % kBitsPerElement;
|
||||
if (nElements != 0) {
|
||||
if (bitCount > 0) {
|
||||
// "Left" shift.
|
||||
memmove(&fBits[nElements], fBits, sizeof(addr_t) * (fElementsCount - nElements));
|
||||
memset(fBits, 0, sizeof(addr_t) * nElements);
|
||||
} else if (bitCount < 0) {
|
||||
// "Right" shift.
|
||||
memmove(fBits, &fBits[nElements], sizeof(addr_t) * (fElementsCount - nElements));
|
||||
memset(&fBits[fElementsCount - nElements], 0, sizeof(addr_t) * nElements);
|
||||
}
|
||||
}
|
||||
|
||||
// If the shift was by a multiple of the element size, nothing more to do.
|
||||
if (nBits == 0)
|
||||
return;
|
||||
|
||||
// One set of bits comes from the "current" element and are shifted in the
|
||||
// direction of the shift; the other set comes from the next-processed
|
||||
// element and are shifted in the opposite direction.
|
||||
if (bitCount > 0) {
|
||||
// "Left" shift.
|
||||
for (ssize_t i = fElementsCount - 1; i >= 0; i--) {
|
||||
addr_t low = 0;
|
||||
if (i != 0)
|
||||
low = fBits[i - 1] >> (kBitsPerElement - nBits);
|
||||
const addr_t high = fBits[i] << nBits;
|
||||
fBits[i] = low | high;
|
||||
}
|
||||
} else if (bitCount < 0) {
|
||||
// "Right" shift.
|
||||
for (size_t i = 0; i < fElementsCount; i++) {
|
||||
const addr_t low = fBits[i] >> nBits;
|
||||
addr_t high = 0;
|
||||
if (i != (fElementsCount - 1))
|
||||
high = fBits[i + 1] << (kBitsPerElement - nBits);
|
||||
fBits[i] = low | high;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ssize_t
|
||||
Bitmap::GetHighestSet() const
|
||||
{
|
||||
int i = fElementsCount - 1;
|
||||
size_t i = fElementsCount - 1;
|
||||
while (i >= 0 && fBits[i] == 0)
|
||||
i--;
|
||||
|
||||
@@ -66,3 +133,5 @@ Bitmap::GetHighestSet() const
|
||||
return log2(fBits[i]) + i * kBitsPerElement;
|
||||
}
|
||||
|
||||
|
||||
} // namespace BKernel
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
#include <cppunit/Test.h>
|
||||
#include <cppunit/TestCaller.h>
|
||||
#include <cppunit/TestSuite.h>
|
||||
#include <stdio.h>
|
||||
#include <TestUtils.h>
|
||||
|
||||
#include "BitmapTest.h"
|
||||
#include "Bitmap.h"
|
||||
|
||||
BitmapTest::BitmapTest(std::string name)
|
||||
: BTestCase(name)
|
||||
{
|
||||
}
|
||||
|
||||
CppUnit::Test*
|
||||
BitmapTest::Suite()
|
||||
{
|
||||
CppUnit::TestSuite *suite = new CppUnit::TestSuite("Bitmap");
|
||||
|
||||
suite->addTest(new CppUnit::TestCaller<BitmapTest>("Bitmap::Resize test",
|
||||
&BitmapTest::ResizeTest));
|
||||
suite->addTest(new CppUnit::TestCaller<BitmapTest>("Bitmap::Shift test",
|
||||
&BitmapTest::ShiftTest));
|
||||
|
||||
return suite;
|
||||
}
|
||||
|
||||
void
|
||||
BitmapTest::ResizeTest()
|
||||
{
|
||||
BKernel::Bitmap bitmap(10);
|
||||
bitmap.Set(6);
|
||||
|
||||
CPPUNIT_ASSERT(bitmap.Get(6));
|
||||
CPPUNIT_ASSERT(!bitmap.Get(5));
|
||||
CPPUNIT_ASSERT(!bitmap.Get(7));
|
||||
|
||||
bitmap.Resize(20);
|
||||
|
||||
CPPUNIT_ASSERT(bitmap.Get(6));
|
||||
CPPUNIT_ASSERT(!bitmap.Get(7));
|
||||
CPPUNIT_ASSERT(!bitmap.Get(19));
|
||||
|
||||
bitmap.Resize(200);
|
||||
bitmap.Set(199);
|
||||
|
||||
CPPUNIT_ASSERT(bitmap.Get(6));
|
||||
CPPUNIT_ASSERT(!bitmap.Get(7));
|
||||
CPPUNIT_ASSERT(!bitmap.Get(19));
|
||||
CPPUNIT_ASSERT(bitmap.Get(199));
|
||||
CPPUNIT_ASSERT(!bitmap.Get(198));
|
||||
}
|
||||
|
||||
void
|
||||
BitmapTest::ShiftTest()
|
||||
{
|
||||
BKernel::Bitmap bitmap(20);
|
||||
bitmap.Set(6);
|
||||
|
||||
CPPUNIT_ASSERT(bitmap.Get(6));
|
||||
CPPUNIT_ASSERT(!bitmap.Get(5));
|
||||
CPPUNIT_ASSERT(!bitmap.Get(7));
|
||||
|
||||
bitmap.Shift(10);
|
||||
|
||||
CPPUNIT_ASSERT(bitmap.Get(16));
|
||||
CPPUNIT_ASSERT(!bitmap.Get(15));
|
||||
CPPUNIT_ASSERT(!bitmap.Get(17));
|
||||
CPPUNIT_ASSERT(!bitmap.Get(6));
|
||||
|
||||
bitmap.Shift(-9);
|
||||
|
||||
CPPUNIT_ASSERT(bitmap.Get(7));
|
||||
CPPUNIT_ASSERT(!bitmap.Get(6));
|
||||
CPPUNIT_ASSERT(!bitmap.Get(8));
|
||||
CPPUNIT_ASSERT(!bitmap.Get(16));
|
||||
|
||||
// Now test cross-element shifting.
|
||||
bitmap.Resize(200);
|
||||
|
||||
CPPUNIT_ASSERT(bitmap.Get(7));
|
||||
CPPUNIT_ASSERT(!bitmap.Get(6));
|
||||
|
||||
bitmap.Shift(100);
|
||||
|
||||
CPPUNIT_ASSERT(!bitmap.Get(7));
|
||||
CPPUNIT_ASSERT(bitmap.Get(107));
|
||||
CPPUNIT_ASSERT(!bitmap.Get(106));
|
||||
|
||||
bitmap.Shift(-100);
|
||||
|
||||
CPPUNIT_ASSERT(bitmap.Get(7));
|
||||
CPPUNIT_ASSERT(!bitmap.Get(107));
|
||||
CPPUNIT_ASSERT(!bitmap.Get(6));
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#ifndef _bitmap_test_h_
|
||||
#define _bitmap_test_h_
|
||||
|
||||
#include <TestCase.h>
|
||||
|
||||
class BitmapTest : public BTestCase {
|
||||
public:
|
||||
BitmapTest(std::string name = "");
|
||||
|
||||
static CppUnit::Test* Suite();
|
||||
|
||||
void ResizeTest();
|
||||
void ShiftTest();
|
||||
};
|
||||
|
||||
#endif // _bitmap_test_h_
|
||||
@@ -6,15 +6,20 @@ UsePrivateHeaders [ FDirName kernel util ] ;
|
||||
UsePrivateHeaders [ FDirName kernel ] ;
|
||||
UseHeaders [ FDirName $(HAIKU_TOP) src tests kits app ] ;
|
||||
|
||||
SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src system kernel util ] ;
|
||||
|
||||
UnitTestLib libkernelutilstest.so
|
||||
: KernelUtilsTestAddon.cpp
|
||||
# AVLTreeMapTest.cpp
|
||||
BOpenHashTableTest.cpp
|
||||
BitmapTest.cpp
|
||||
SinglyLinkedListTest.cpp
|
||||
DoublyLinkedListTest.cpp
|
||||
VectorMapTest.cpp
|
||||
VectorSetTest.cpp
|
||||
VectorTest.cpp
|
||||
|
||||
Bitmap.cpp
|
||||
: [ TargetLibstdc++ ] be
|
||||
;
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
//#include "AVLTreeMapTest.h"
|
||||
#include "BOpenHashTableTest.h"
|
||||
#include "BitmapTest.h"
|
||||
#include "DoublyLinkedListTest.h"
|
||||
#include "SinglyLinkedListTest.h"
|
||||
#include "VectorMapTest.h"
|
||||
@@ -14,6 +15,7 @@ BTestSuite* getTestSuite() {
|
||||
BTestSuite *suite = new BTestSuite("KernelUtils");
|
||||
// suite->addTest("AVLTreeMap", AVLTreeMapTest::Suite());
|
||||
suite->addTest("BOpenHashTable", BOpenHashTableTest::Suite());
|
||||
suite->addTest("Bitmap", BitmapTest::Suite());
|
||||
suite->addTest("SinglyLinkedList", SinglyLinkedListTest::Suite());
|
||||
suite->addTest("DoublyLinkedList", DoublyLinkedListTest::Suite());
|
||||
suite->addTest("VectorMap", VectorMapTest::Suite());
|
||||
|
||||
Reference in New Issue
Block a user