[network/libktorrent] src/util: ABI break: remove manual memory management from BitSet

Jack Hill <[email protected]>
Newsgroups gmane.comp.kde.cvs
Message-ID <[email protected]>
Git commit efd12f9a7306b7384a96453d134dfab06944ae56 by Jack Hill.
Committed on 28/07/2026 at 19:25.
Pushed by jackh into branch 'master'.

ABI break: remove manual memory management from BitSet

Use QList instead of raw array allocation.

Also now use default implementation of copy constructor and destructor.
Implemented move operator too.

M  +43   -48   src/util/bitset.cpp
M  +30   -11   src/util/bitset.h

https://invent.kde.org/network/libktorrent/-/commit/efd12f9a7306b7384a96453d134dfab06944ae56

diff --git a/src/util/bitset.cpp b/src/util/bitset.cpp
index 31670121..c695f780 100644
--- a/src/util/bitset.cpp
+++ b/src/util/bitset.cpp
@@ -3,65 +3,60 @@
 
     SPDX-License-Identifier: GPL-2.0-or-later
 */
+
 #include "bitset.h"
+
 #include <algorithm>
 #include <cstring>
+#include <utility>
 
 namespace bt
 {
+namespace
+{
+constexpr auto NumBytesFromNumBits(const auto num_bits)
+{
+    return (num_bits >> 3) + (((num_bits & 7) > 0) ? 1 : 0);
+}
+}
+
 BitSet BitSet::null;
 
 BitSet::BitSet(Uint32 num_bits)
     : num_bits(num_bits)
-    , num_bytes((num_bits >> 3) + (((num_bits & 7) > 0) ? 1 : 0))
-    , data(new Uint8[num_bytes])
-    , num_on(0)
+    , data(NumBytesFromNumBits(num_bits))
 {
-    std::fill(data, data + num_bytes, 0x00);
 }
 
 BitSet::BitSet(const Uint8 *d, Uint32 num_bits)
     : num_bits(num_bits)
-    , num_bytes((num_bits >> 3) + (((num_bits & 7) > 0) ? 1 : 0))
-    , data(new Uint8[num_bytes])
-    , num_on(0)
 {
-    memcpy(data, d, num_bytes);
+    data.assign(d, d + NumBytesFromNumBits(num_bits));
     updateNumOnBits();
 }
 
-BitSet::BitSet(const BitSet &bs)
-    : num_bits(bs.num_bits)
-    , num_bytes(bs.num_bytes)
-    , data(new Uint8[num_bytes])
-    , num_on(bs.num_on)
-{
-    std::copy(bs.data, bs.data + num_bytes, data);
-}
-
-BitSet::~BitSet()
+BitSet::BitSet(BitSet &&bs)
+    : num_bits(std::exchange(bs.num_bits, 0))
+    , data(std::move(bs.data))
+    , num_on(std::exchange(bs.num_on, 0))
 {
-    delete[] data;
 }
 
 void BitSet::updateNumOnBits()
 {
     num_on = 0;
     Uint32 i = 0;
-    while (i < num_bytes) {
+    while (i < data.size()) {
         num_on += std::popcount(data[i]);
         i++;
     }
 }
 
-BitSet &BitSet::operator=(const BitSet &bs)
+BitSet &BitSet::operator=(BitSet &&bs)
 {
-    delete[] data;
-    num_bytes = bs.num_bytes;
-    num_bits = bs.num_bits;
-    data = new Uint8[num_bytes];
-    std::copy(bs.data, bs.data + num_bytes, data);
-    num_on = bs.num_on;
+    num_bits = std::exchange(bs.num_bits, 0);
+    data = std::move(bs.data);
+    num_on = std::exchange(bs.num_on, 0);
     return *this;
 }
 
@@ -69,18 +64,18 @@ const Uint8 tail_mask_lookup[8] = {0xFF, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7
 
 void BitSet::invert()
 {
-    if (num_bytes <= 0) {
+    if (data.size() <= 0) {
         return;
     }
 
     num_on = 0;
     Uint32 i = 0;
-    while (i < num_bytes - 1) {
+    while (i < data.size() - 1) {
         data[i] = ~data[i];
         num_on += std::popcount(data[i]);
         i++;
     }
-    // i == num_bytes-1
+    // i == data.size()-1
     data[i] = ~data[i] & tail_mask_lookup[num_bits & 7];
     num_on += std::popcount(data[i]);
 }
@@ -88,7 +83,7 @@ void BitSet::invert()
 BitSet &BitSet::operator-=(const BitSet &bs)
 {
     num_on = 0;
-    for (Uint32 i = 0; i < num_bytes; i++) {
+    for (Uint32 i = 0; i < data.size(); i++) {
         data[i] &= ~(data[i] & bs.data[i]);
         num_on += std::popcount(data[i]);
     }
@@ -102,7 +97,7 @@ BitSet BitSet::operator-(const BitSet &bs) const
 
 void BitSet::setAll(bool on)
 {
-    std::fill(data, data + num_bytes, on ? 0xFF : 0x00);
+    data.fill(on ? 0xFF : 0x00);
     num_on = on ? num_bits : 0;
 }
 
@@ -117,7 +112,7 @@ void BitSet::orBitSet(const BitSet &other)
 
     if (num_bits == other.num_bits) {
         // best case
-        for (Uint32 i = 0; i < num_bytes; i++) {
+        for (Uint32 i = 0; i < data.size(); i++) {
             data[i] |= other.data[i];
             num_on += std::popcount(data[i]);
         }
@@ -126,24 +121,24 @@ void BitSet::orBitSet(const BitSet &other)
 
     // process till the end of other data or last-1 byte in our data
     // whatether comes first
-    for (Uint32 i = 0; i < qMin(num_bytes - 1, other.num_bytes); i++) {
+    for (Uint32 i = 0; i < qMin(data.size() - 1, other.data.size()); i++) {
         data[i] |= other.data[i];
         num_on += std::popcount(data[i]);
     }
 
     // if last-1 not reached yet then the end of other data is reached
     // so just add std::popcount till last-1 byte
-    for (Uint32 i = other.num_bytes; i < num_bytes - 1; i++) {
+    for (Uint32 i = other.data.size(); i < data.size() - 1; i++) {
         num_on += std::popcount(data[i]);
     }
 
     // if other has matching byte for our last byte - OR it with proper mask
-    if (other.num_bytes >= num_bytes) {
-        data[num_bytes - 1] = (data[num_bytes - 1] | other.data[num_bytes - 1]) & tail_mask_lookup[num_bytes & 7];
+    if (other.data.size() >= data.size()) {
+        data[data.size() - 1] = (data[data.size() - 1] | other.data[data.size() - 1]) & tail_mask_lookup[data.size() & 7];
     }
 
     // count bits set in last byte
-    num_on += std::popcount(data[num_bytes - 1]);
+    num_on += std::popcount(data[data.size() - 1]);
 }
 
 void BitSet::andBitSet(const BitSet &other)
@@ -152,7 +147,7 @@ void BitSet::andBitSet(const BitSet &other)
 
     if (num_bits == other.num_bits) {
         // best case
-        for (Uint32 i = 0; i < num_bytes; i++) {
+        for (Uint32 i = 0; i < data.size(); i++) {
             data[i] &= other.data[i];
             num_on += std::popcount(data[i]);
         }
@@ -162,13 +157,13 @@ void BitSet::andBitSet(const BitSet &other)
     // we expect 0's at the tail of last byte (if any)
     // so just AND matching bytes and clear the others
     // no need to worry about mask for last byte
-    for (Uint32 i = 0; i < qMin(num_bytes, other.num_bytes); i++) {
+    for (Uint32 i = 0; i < qMin(data.size(), other.data.size()); i++) {
         data[i] &= other.data[i];
         num_on += std::popcount(data[i]);
     }
 
-    if (num_bytes > other.num_bytes) {
-        memset(data + other.num_bytes, 0, num_bytes - other.num_bytes);
+    if (data.size() > other.data.size()) {
+        std::fill(data.begin() + other.data.size(), data.end(), 0);
     }
 }
 
@@ -176,7 +171,7 @@ bool BitSet::includesBitSet(const BitSet &other) const
 {
     if (num_bits == other.num_bits) {
         // best case
-        for (Uint32 i = 0; i < num_bytes; i++) {
+        for (Uint32 i = 0; i < data.size(); i++) {
             if ((data[i] | other.data[i]) != data[i]) {
                 return false;
             }
@@ -186,16 +181,16 @@ bool BitSet::includesBitSet(const BitSet &other) const
 
     // process till the end of other data or last-1 byte in our data
     // whatether comes first
-    for (Uint32 i = 0; i < qMin(num_bytes - 1, other.num_bytes); i++) {
+    for (Uint32 i = 0; i < qMin(data.size() - 1, other.data.size()); i++) {
         if ((data[i] | other.data[i]) != data[i]) {
             return false;
         }
     }
 
     // if other has matching byte for our last byte - OR it with proper mask
-    if (other.num_bytes >= num_bytes) {
-        const Uint8 d = data[num_bytes - 1];
-        if (((d | other.data[num_bytes - 1]) & tail_mask_lookup[num_bytes & 7]) != d) {
+    if (other.data.size() >= data.size()) {
+        const Uint8 d = data[data.size() - 1];
+        if (((d | other.data[data.size() - 1]) & tail_mask_lookup[data.size() & 7]) != d) {
             return false;
         }
     }
@@ -214,6 +209,6 @@ bool BitSet::operator==(const BitSet &bs) const
         return false;
     }
 
-    return memcmp(data, bs.data, num_bytes) == 0;
+    return data == bs.data;
 }
 }
diff --git a/src/util/bitset.h b/src/util/bitset.h
index 29113130..758a50d2 100644
--- a/src/util/bitset.h
+++ b/src/util/bitset.h
@@ -8,6 +8,8 @@
 
 #include <bit>
 
+#include <QList>
+
 #include "constants.h"
 #include <ktorrent_export.h>
 
@@ -23,9 +25,9 @@ namespace bt
  */
 class KTORRENT_EXPORT BitSet
 {
-    Uint32 num_bits, num_bytes;
-    Uint8 *data;
-    Uint32 num_on;
+    Uint32 num_bits = 0;
+    QList<Uint8> data;
+    Uint32 num_on = 0;
 
 public:
     /*!
@@ -45,8 +47,18 @@ public:
      * Copy constructor.
      * \param bs BitSet to copy
      */
-    BitSet(const BitSet &bs);
-    virtual ~BitSet();
+    BitSet(const BitSet &bs) = default;
+
+    /*!
+     * Move constructor.
+     * \param bs BitSet to move
+     */
+    BitSet(BitSet &&bs);
+
+    /*!
+     * Destructor
+     */
+    ~BitSet() = default;
 
     //! See if the BitSet is null
     [[nodiscard]] bool isNull() const
@@ -72,7 +84,7 @@ public:
 
     [[nodiscard]] Uint32 getNumBytes() const
     {
-        return num_bytes;
+        return data.size();
     }
     [[nodiscard]] Uint32 getNumBits() const
     {
@@ -80,11 +92,11 @@ public:
     }
     [[nodiscard]] const Uint8 *getData() const
     {
-        return data;
+        return data.data();
     }
     Uint8 *getData()
     {
-        return data;
+        return data.data();
     }
 
     //! Get the number of on bits
@@ -122,11 +134,18 @@ public:
     [[nodiscard]] bool includesBitSet(const BitSet &other) const;
 
     /*!
-     * Assignment operator.
+     * Copy assignment operator.
      * \param bs BitSet to copy
      * \return *this
      */
-    BitSet &operator=(const BitSet &bs);
+    BitSet &operator=(const BitSet &bs) = default;
+
+    /*!
+     * Move assignment operator.
+     * \param bs BitSet to move
+     * \return *this
+     */
+    BitSet &operator=(BitSet &&bs);
 
     /*!
      * Subtraction assignment operator.
@@ -187,7 +206,7 @@ inline void BitSet::set(Uint32 i, bool on)
         return;
     }
 
-    Uint8 *d = data + (i >> 3);
+    Uint8 *d = data.data() + (i >> 3);
     num_on -= std::popcount(*d);
     if (on) {
         *d |= set_on_lookup[i & 7];
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.