C++ 位拷贝

编程入门 行业动态 更新时间:2024-10-09 04:21:25

C++ 位拷贝

C++ 位拷贝

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

#include <cstdint>
#include <cstdlib>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){// 不需要错位拷贝if (dst_start_bit == src_start_bit){// 起始位为0,则退化为类似memcpyif (src_start_bit == 0)return __bitcpy(reinterpret_cast<uint8_t*>(dst_bitmap),reinterpret_cast<const uint8_t*>(src_bitmap),bit_length);// 起始位不为0,起始字节需要额外处理。elsereturn __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: 计算源数据和目标数据的起始位均为0的情况。此时除了最后一个可能不完整的字节数据之外,其他部分*         使用memcpy即可。* * @param dst_ptr 目标地址* @param src_ptr 源地址* @param bit_length 要拷贝的位数*/static void __bitcpy(uint8_t* dst_ptr, const uint8_t* src_ptr, uint32_t bit_length){//将前面的数据直接使用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: 计算源数据和目标数据起始位相同的情况。此时不需要错位复制。起始字节和终止字节单独处理,中间使*         用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;}//处理第一个字节uint8_t mask = (1U << start_bit) - 1;*dst_ptr     = (*src_ptr & (~mask)) | (*dst_ptr & mask);bit_length -= 8 - start_bit;//此时剩余数据的拷贝退化为起始位为0的特殊情况__bitcpy(++dst_ptr, ++src_ptr, bit_length);}/*** @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++;// 如果需要右移,则目标地址的第一个数据块未被全部填充,计算尚未被填充的起始位。此时后面的数据拷贝又// 退化为了需要左移elsestart_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);}
};

更多推荐

C++ 位拷贝

本文发布于:2023-12-03 08:07:57,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1653032.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!