动态范围Bitmap的参考实现

前言

在实现DDS的过程中需要一个bitmap来快速查找某个序列号的message状态,这里参考fastdds中的bitmap实现,在此分析并记录一下。

关键词:bitmap,__builtin_clz

需求

  • 能够接收自定义的sequence类型
  • 具有动态范围

实现

首先BitmapRange使用了三个模板:

template<class T, class Diff = DiffFunction<T>, uint32_t NBITS = 256>
class BitmapRange
{};

模板T可以是自定义的序列号,要求T实现“>=”运算符且能够与uint32_t进行加法运算;
Diff是针对类型T实现的仿函数,用于计算两个T之间的差距;
NBITS指定Bitmap的最大范围,默认是256个元素;

Diff的默认仿函数实现如下:

template <class T>
struct DiffFunction
{
    constexpr auto operator () (T a, T b) const -> decltype(a - b)
    {
        return a - b;
    }
};

BitmapRange采用uint32_t作为一个单元,一个单元可以容纳32个元素,所需要的单元数量根据最大范围NBITS进行计算:

 #define NITEMS ((NBITS + 31u) / 32u)  //bitmap需要多少个uint32_t

这些uint32_t单元储存在一个array中,成员变量的名字是bitmap_:

using bitmap_type = std::array<uint32_t, NITEMS>;
...
bitmap_type bitmap_;

BitmapRange的动态范围通过一个base_和range_max_实现,所有的元素i必须满足 base_<= i <=range_max_;

BitmapRange的主要接口有:

BitmapRange(); 
explicit BitmapRange(T base);
void base_update( T base);
T max();
T min();
bool is_set(const T& item);
bool add(const T& item);  

BitmapRange()

默认构造函数,构造一个空的bitmap,base设置为0。

    BitmapRange() noexcept
        : base_()
        , range_max_(base_ + (NBITS - 1))
        , bitmap_()
        , num_bits_(0u)
    {
    }

BitmapRange(T base)

带base的构造函数,和默认构造函数类似,只不过会设置base成传入的值。

void base_update( T base)

移动bitmap窗口,保留原有的数据

    void base_update(T base) noexcept
    {
        if (base == base_){return;}
        Diff d_func; //用此函数计算新的base和当前base_的差距
        if (base > base_)
        {
            // bitmap所有内容左移n_bits位
            uint32_t n_bits = d_func(base, base_);
            shift_map_left(n_bits);
        }
        else
        {
            // bitmap所有内容右移n_bits位
            uint32_t n_bits = d_func(base_, base);
            shift_map_right(n_bits);
        }
        base_ = base;
        range_max_ = base_ + (NBITS - 1);
    }

这里介绍下左移操作。

    void shift_map_left(uint32_t n_bits)
    {
        if (n_bits >= num_bits_)
        {
            // 移动位数超过当前最高有效位,全部清零
            num_bits_ = 0;
            bitmap_.fill(0UL);
        }
        else
        {
            num_bits_ -= n_bits;// 更新最高有效位数量,减去左移距离
            uint32_t n_items = n_bits >> 5; // 移动位多少个完整单元
            n_bits &= 31UL; // 模32
            if (n_bits == 0)
            {
            // 左移刚好32整数倍,n_items个单元整体前移,后面补零
            // aaaaaaaa bbbbbbbb cccccccc dddddddd  ->block0
            // eeeeeeee ffffffff gggggggg hhhhhhhh  ->block1
            // iiiiiiii jjjjjjjj kkk00000 00000000  //82
            // 00000000 00000000 00000000 00000000  ->block3
            //  shift 32 bits
            // eeeeeeee ffffffff gggggggg hhhhhhhh  ->block0
            // iiiiiiii jjjjjjjj kkk00000 00000000  //50
            // 00000000 00000000 00000000 00000000  ->block2
            // 00000000 00000000 00000000 00000000  ->block3
                std::copy(bitmap_.begin() + n_items, bitmap_.end(), bitmap_.begin());
                std::fill_n(bitmap_.rbegin(), n_items, 0);
            }
            else
            {
            // aaaaaaaa bbbbbbbb cccccccc dddddddd ->block0
            // eeeeeeee ffffffff gggggggg hhhhhhhh ->block1
            // iiiiiiii jjjjjjjj kkkkkkkk llllllll ->block2
            // mmmmmmmm nnnnnnnn oooooooo ppp00000 ->block3
            //  shift 45 bits,1 block, mod 13
            // fffggggg ggghhhhh hhhiiiii iiijjjjj ->block0
            // jjjkkkkk kkklllll lllmmmmm mmmnnnnn ->block1
            // nnnooooo oooppp00 00000000 00000000 ->block2
            // 00000000 00000000 00000000 00000000 ->block3
                uint32_t overflow_bits = 32u - n_bits;
                size_t last_index = NITEMS - 1u;
                // 非完整单元向前移动,覆盖需要左移的完整单元,非完整移动的单元从第n_items块开始
                for (size_t i = 0, n = n_items; n < last_index; i++, n++)
                {
                	// 第n块左移n_bits位,剩余的用n+1块高位前(32-n_bits)补上           
                    bitmap_[i] = (bitmap_[n] << n_bits) | (bitmap_[n + 1] >> overflow_bits);
                }
                // 最后一块只用左移n_bits,后面的没得补了
                bitmap_[last_index - n_items] = bitmap_[last_index] << n_bits;
                
                // 尾部的n_items个单元补零
                std::fill_n(bitmap_.rbegin(), n_items, 0);
            }
        }
    }

T max()

返回当前bitmap里面被设置成1的最高位,根据当前base和最高位num_bits_计算得到。

    T max() const noexcept
    {
        return base_ + (num_bits_ - 1); 
    }

T min()

返回当前bitmap里面被设置成1的最低位,这里需要遍历每个单元,寻找第一个非零单元的第一个非零bit位。

    T min() const noexcept
    {
        T item = base_; //item从base开始,每遍历了一个单元,item就偏移一个单元的长度,即32位
        uint32_t n_longs = (num_bits_ + 31u) / 32u; //需要遍历的单元数量
        for (uint32_t i = 0; i < n_longs; i++)
        {
            uint32_t bits = bitmap_[i]; 
            if (bits)  //这个单元不是0,那么寻找第一个非零位
            {
            	//使用gcc内置函数__builtin_clz来统计uint32_t高位有几个连续的0;
                uint32_t offset = static_cast<uint32_t>(__builtin_clz(bits));
                //最低位就是当前单元起始位加上高位0的数目
                return item + offset;
            }
            item = item + 32u; //item偏移一个单元的长度,即32位
        }
        return base_;
    }

bool is_set(const T& item)

判断item在bitmap中的状态

    bool is_set(const T& item) const noexcept
    {
    	//确保item在范围内
        if ((item >= base_) && (range_max_ >= item))
        {
            Diff d_func;
            uint32_t diff = d_func(item, base_); //计算item和base的偏移量
            if (diff < num_bits_) //如果超出了当前已设置位1的最高位,那肯定返回false
            {
                uint32_t pos = diff >> 5;  //右移5位,得到所在单元的编号
                diff &= 31UL;   //取32的模,得到高位向低位的偏移量
                return (bitmap_[pos] & (1UL << (31UL - diff))) != 0;  //1左移到目标位,然后和bitmap单元与运算
            }
        }
        return false;
    }

bool add(const T& item)

向bitmap中添加item

    bool add(const T& item) noexcept
    {
    //与is_set类似
        if ((item >= base_) && (range_max_ >= item))
        {
            Diff d_func;
            uint32_t diff = d_func(item, base_);
            num_bits_ = std::max(diff + 1, num_bits_); //更新最高设置位
            uint32_t pos = diff >> 5;
            diff &= 31u;
            bitmap_[pos] |= (1u << (31u - diff));  //1左移并与bitmap单元或运算
            return true;
        }

        return false;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

灰灰h

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值