• C++ 位拷贝


    分享一个C++的按位拷贝的代码。
    由于标准库中所有的拷贝都是以字节为单位的,而没有按位进行拷贝的。最近碰到了一个需要按位操作数据的情况,写了一段代码。如有错误,望请指摘。

    #include 
    #include 
    
    class BitOperator
    {
    public:
        /**
         * @brief: 按位拷贝。如从{0x00, 0xff, 0xff, 0xff}中的第2位开始,拷贝12位到{0x00, 0x00, 0x00, 0x00}
         *         的第5位开始。则得到:{0x00, 0xf8, 0x01, 0x00}。
         * 
         *            低地址                                高地址
         *              ↓                                     ↓
         *         src: 0000 0000 1111 1111 1111 1111 1111 1111  =>  {0x00, 0xff, 0xff, 0xff}
         *                -- ---- ---- --
         *                        ↘
         *                    ___ ____ ____ _
         *         dst: 0000 0000 0000 0000 0000 0000 0000 0000  =>  {0x00, 0x00, 0x00, 0x00}
         * 
         *         res: 0000 0000 0001 1111 1000 0000 0000 0000  =>  {0x00, 0xf8, 0x01, 0x00}
         * 
         * @param dst_bitmap 目标地址
         * @param src_bitmap 源地址
         * @param bit_length 要拷贝的位数
         * @param dst_start_bit 起始位在目标字节中的index。该起始位会被包括在拷贝范围内。该值不应该超过7。
         * @param src_start_bit 起始位在源地址指向的字节中的index。该起始位的位数据会被覆盖。该值不应该超过7。
         */
        static inline void bitcpy(
            void*       dst_bitmap,
            const void* src_bitmap,
            uint32_t    bit_length,
            uint8_t     dst_start_bit = 0,
            uint8_t     src_start_bit = 0)
        {
            // 不需要错位拷贝,则起始和终止字节额外处理,中间使用memcpy
            if (dst_start_bit == src_start_bit)
            {
                return __bitcpy(
                    reinterpret_cast<uint8_t*>(dst_bitmap),
                    reinterpret_cast<const uint8_t*>(src_bitmap),
                    bit_length,
                    src_start_bit);
            }
            // 需要错位拷贝。
            // 需要源数据右移后拷贝至目的地址。
            else if (dst_start_bit < src_start_bit)
            {
                uint8_t diff = src_start_bit - dst_start_bit;
                __bitcpy<RIGHT_SHIFT>(
                    reinterpret_cast<uint32_t*>(dst_bitmap),
                    reinterpret_cast<const uint32_t*>(src_bitmap),
                    bit_length,
                    diff,
                    src_start_bit);
            }
            // 需要源数据左移后拷贝至目的地址。
            else
            {
                uint8_t diff = dst_start_bit - src_start_bit;
                __bitcpy<LEFT_SHIFT>(
                    reinterpret_cast<uint32_t*>(dst_bitmap),
                    reinterpret_cast<const uint32_t*>(src_bitmap),
                    bit_length,
                    diff,
                    src_start_bit);
            }
        }
    
        /**
         * @brief: 位拷贝的安全版本,会对输入参数进行检测。如果参数正确则返回true。否则返回false。
         * 
         * @param dst_bitmap 目标地址
         * @param src_bitmap 源地址
         * @param bit_length 要拷贝的位数
         * @param dst_start_bit 起始位在目标字节中的index。该起始位会被包括在拷贝范围内。该值不应该超过7。
         * @param src_start_bit 起始位在源地址指向的字节中的index。该起始位的位数据会被覆盖。该值不应该超过7。
         * @return true 正确拷贝
         * @return false 输入参数有问题
         */
        static bool bitcpy_s(
            void*       dst_bitmap,
            const void* src_bitmap,
            uint32_t    bit_length,
            uint8_t     dst_start_bit = 0,
            uint8_t     src_start_bit = 0)
        {
            // 输入检测
            if (dst_bitmap == nullptr)
                return false;
            if (src_bitmap == nullptr)
                return false;
            if (dst_start_bit > 7)
                return false;
            if (src_start_bit > 7)
                return false;
            if (bit_length == 0)
                return true;
    
            bitcpy(dst_bitmap, src_bitmap, bit_length, dst_start_bit, src_start_bit);
            return true;
        }
    
    private:
        // 定义左移为true, 右移为false。
        constexpr static bool LEFT_SHIFT  = true;
        constexpr static bool RIGHT_SHIFT = false;
    
    private:
        /**
         * @brief: 计算选中从start位开始,bit_length长度的位的掩码。将数据与该掩码相与即可选中指定位。
         *         比如从第0位开始,选中8位,则掩码为:0x0000 0000 0000 00FF。
         * 
         * @param start 起始位
         * @param bit_length 位长度
         * @return uint64_t 掩码
         */
        inline static uint64_t calculate_mask(uint8_t start, uint8_t bit_length)
        {
            uint64_t res = 1ULL << start;
            return (res << bit_length) - res;
        }
    
        /**
         * @brief: 计算源数据和目标数据起始位相同的情况。此时不需要错位复制。起始字节和终止字节单独处理,中间使
         *         用memcpy即可。
         * 
         * @param dst_ptr 目标地址
         * @param src_ptr 源地址
         * @param bit_length 要拷贝的位数
         * @param start_bit 起始位
         */
        static void __bitcpy(
            uint8_t*       dst_ptr,
            const uint8_t* src_ptr,
            uint32_t       bit_length,
            uint8_t        start_bit)
        {
            // 如果要拷贝的数据在一个字节中,则直接选中拷贝。
            if (start_bit + bit_length <= 8)
            {
                auto mask = calculate_mask(start_bit, bit_length);
                *dst_ptr  = (*src_ptr & mask) | (*dst_ptr & (~mask));
                return;
            }
            //如果起始位不为0,则第一个字节需要单独处理。
            if (start_bit != 0)
            {
                uint8_t mask = (1U << start_bit) - 1;
                *dst_ptr = (*src_ptr & mask) | (*dst_ptr & (~mask));
                bit_length -= 8 - start_bit;
                ++dst_ptr;
                ++src_ptr;
            }
            //中间的数据直接使用memcpy进行拷贝
            auto byte_size = bit_length >> 3;
            memcpy(dst_ptr, src_ptr, byte_size);
            //如果最后有不完整的字节数据需要拷贝,进行处理。
            bit_length &= 7;
            if (bit_length != 0)
            {
                dst_ptr += byte_size;
                src_ptr += byte_size;
                uint8_t mask = (1 << bit_length) - 1;
                *dst_ptr = (*src_ptr & mask) | (*dst_ptr & (~mask));
            }
        }
    
        /**
         * @brief: 以4字节为一个数据块,拷贝一个数据块。将源地址起始的4字节内需要拷贝的数据,全部拷贝至目标地址
         *         指向的8字节空间中。因为目标地址是按8字节进行考虑的,因此不用对源数据进行切割。
         *
         * @tparam _Left_Shift 拷贝时需要左移还是右移
         * @param dst_bitmap 目标地址
         * @param src_bitmap 源地址
         * @param bit_length 要拷贝的位数
         * @param start_diff 源地址和目标地址的起始位之差。
         * @param src_start_bit 起始位在源地址指向的字节中的index。该起始位的位数据会被覆盖。该值不应该超过7。
         */
        template <bool _Left_Shift>
        inline static void copy_4_bytes(
            void*           dst_bitmap,
            const uint32_t* src_bitmap,
            uint8_t         bit_length,
            uint8_t         start_diff,
            uint8_t         src_start_bit = 0)
        {
            // 从源地址获取数据
            uint64_t temp = *src_bitmap;
            uint64_t mask = calculate_mask(src_start_bit, bit_length);
            temp &= mask;
            // 左移或右移掩码与数据,使其与目标地址所在的位置对齐
            if constexpr (_Left_Shift)
            {
                temp <<= start_diff;
                mask <<= start_diff;
            }
            else
            {
                temp >>= start_diff;
                mask >>= start_diff;
            }
            //取出目标地址的数据,并将源数据中选中的部分覆盖至目标数据中,最后拷贝回目标地址
            uint64_t res = (*reinterpret_cast<uint64_t*>(dst_bitmap)) & (~mask);
            res |= temp;
            *reinterpret_cast<uint64_t*>(dst_bitmap) = res | temp;
        }
    
        /**
         * @brief: 处理需要错位拷贝的情况。以4字节为一个数据块进行处理以减少循环次数。
         *
         * @tparam _Left_Shift 拷贝时需要左移还是右移
         * @param dst_ptr 目标地址
         * @param src_ptr 源地址
         * @param bit_length 要拷贝的位数
         * @param start_diff 源地址和目标地址的起始位之差。
         * @param src_start_bit 起始位在源地址指向的字节中的index。该起始位的位数据会被覆盖。该值不应该超过7。
         */
        template <bool _Left_Shift>
        static void __bitcpy(
            uint32_t*       dst_ptr,
            const uint32_t* src_ptr,
            uint32_t        bit_length,
            uint8_t         start_diff,
            uint8_t         src_start_bit)
        {
            // 如果待拷贝的数据均在一个数据块内,直接进行拷贝。
            if (src_start_bit + bit_length <= 32)
            {
                copy_4_bytes<_Left_Shift>(dst_ptr, src_ptr, bit_length, start_diff, src_start_bit);
                return;
            }
            // 拷贝第一个数据块。
            copy_4_bytes<_Left_Shift>(
                dst_ptr, src_ptr++, 32 - src_start_bit, start_diff, src_start_bit);
            bit_length -= 32 - src_start_bit;
            // 如果需要左移,则目标地址的第一个数据块已经被填充完毕,指向下一个数据块
            if constexpr (_Left_Shift)
                dst_ptr++;
            // 如果需要右移,则目标地址的第一个数据块未被全部填充,计算尚未被填充的起始位。此时后面的数据拷贝又
            // 退化为了需要左移
            else
                start_diff = 32 - start_diff;
            // 拷贝中间的数据块.
            while (bit_length > 32)
            {
                copy_4_bytes<LEFT_SHIFT>(dst_ptr++, src_ptr++, 32, start_diff);
                bit_length -= 32;
            }
            // 拷贝最后一个数据块.
            copy_4_bytes<LEFT_SHIFT>(dst_ptr, src_ptr, bit_length, start_diff);
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
  • 相关阅读:
    计算机视觉图像处理面试笔试题整理——光流算法
    MacOS下VMware Fusion配置静态IP
    基于安卓android微信小程序的个人管理小程序
    md编辑器常用语法模板
    12、Urban Radiance Fields
    unity 使用声网(Agora)实现语音通话
    laravel composer require require-dev和APP_ENV的使用场景
    docker搭建maven私服
    Java-GUI编程之事件处理
    v-if、v-for、列表的过滤与排序、强制绑定class和style、收集表单信息
  • 原文地址:https://blog.csdn.net/qq_44844115/article/details/134062823