From 88275138bae13f25a33a610ba51d116f4a593b81 Mon Sep 17 00:00:00 2001 From: Augustin Cavalier Date: Thu, 24 Mar 2022 21:27:36 -0400 Subject: [PATCH] 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 --- headers/private/kernel/util/Bitmap.h | 57 +++++---- src/system/kernel/util/Bitmap.cpp | 115 ++++++++++++++---- src/tests/system/kernel/util/BitmapTest.cpp | 95 +++++++++++++++ src/tests/system/kernel/util/BitmapTest.h | 16 +++ src/tests/system/kernel/util/Jamfile | 5 + .../kernel/util/KernelUtilsTestAddon.cpp | 2 + 6 files changed, 241 insertions(+), 49 deletions(-) create mode 100644 src/tests/system/kernel/util/BitmapTest.cpp create mode 100644 src/tests/system/kernel/util/BitmapTest.h diff --git a/headers/private/kernel/util/Bitmap.h b/headers/private/kernel/util/Bitmap.h index 08114715db..e6f6d69627 100644 --- a/headers/private/kernel/util/Bitmap.h +++ b/headers/private/kernel/util/Bitmap.h @@ -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, pdziepak@quarnos.org + * Augustin Cavalier */ #ifndef KERNEL_UTIL_BITMAP_H #define KERNEL_UTIL_BITMAP_H -#include +#ifdef _KERNEL_MODE +# include +#else +# include +#endif #include +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 diff --git a/src/system/kernel/util/Bitmap.cpp b/src/system/kernel/util/Bitmap.cpp index 4844abb1a1..af42ec4e2b 100644 --- a/src/system/kernel/util/Bitmap.cpp +++ b/src/system/kernel/util/Bitmap.cpp @@ -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, pdziepak@quarnos.org + * Augustin Cavalier */ - - #include -#include - +#include #include #include -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 diff --git a/src/tests/system/kernel/util/BitmapTest.cpp b/src/tests/system/kernel/util/BitmapTest.cpp new file mode 100644 index 0000000000..174c9ee4d1 --- /dev/null +++ b/src/tests/system/kernel/util/BitmapTest.cpp @@ -0,0 +1,95 @@ +#include +#include +#include +#include +#include + +#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("Bitmap::Resize test", + &BitmapTest::ResizeTest)); + suite->addTest(new CppUnit::TestCaller("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)); +} diff --git a/src/tests/system/kernel/util/BitmapTest.h b/src/tests/system/kernel/util/BitmapTest.h new file mode 100644 index 0000000000..b44b9bf71b --- /dev/null +++ b/src/tests/system/kernel/util/BitmapTest.h @@ -0,0 +1,16 @@ +#ifndef _bitmap_test_h_ +#define _bitmap_test_h_ + +#include + +class BitmapTest : public BTestCase { +public: + BitmapTest(std::string name = ""); + + static CppUnit::Test* Suite(); + + void ResizeTest(); + void ShiftTest(); +}; + +#endif // _bitmap_test_h_ diff --git a/src/tests/system/kernel/util/Jamfile b/src/tests/system/kernel/util/Jamfile index 7b42652253..eda5640e40 100644 --- a/src/tests/system/kernel/util/Jamfile +++ b/src/tests/system/kernel/util/Jamfile @@ -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 ; diff --git a/src/tests/system/kernel/util/KernelUtilsTestAddon.cpp b/src/tests/system/kernel/util/KernelUtilsTestAddon.cpp index ad43486ab6..05b1891010 100644 --- a/src/tests/system/kernel/util/KernelUtilsTestAddon.cpp +++ b/src/tests/system/kernel/util/KernelUtilsTestAddon.cpp @@ -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());