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:
Augustin Cavalier
2022-03-30 20:17:50 +00:00
committed by waddlesplash
parent 9dacea2ee7
commit 88275138ba
6 changed files with 241 additions and 49 deletions
+31 -26
View File
@@ -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
+92 -23
View File
@@ -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));
}
+16
View File
@@ -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_
+5
View File
@@ -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());