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){// 不需要错位拷贝,则起始和终止字节额外处理,中间使用memcpyif (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++;// 如果需要右移,则目标地址的第一个数据块未被全部填充,计算尚未被填充的起始位。此时后面的数据拷贝又// 退化为了需要左移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);}
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/120551.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Cloak斗篷、AB轮询收款科技详解,FP独立站原来可以这样玩!

大多数独立站玩家以前是通过主流的PP、stripe直接进行收款&#xff0c;但因特货类产品客诉率及拒付率等一系列原因&#xff0c;PP、stripe都不支持用于这类产品收款&#xff0c;冒然使用会导致收款账号被查封&#xff0c;需要180天才能回笼资金&#xff0c;严重一点甚至账号直接…

python+requests接口自动化测试框架

1、首先&#xff0c;我们先来理一下思路。 正常的接口测试流程是什么&#xff1f; 脑海里的反应是不是这样的&#xff1a; 确定测试接口的工具 —> 配置需要的接口参数 —> 进行测试 —> 检查测试结果&#xff08;有的需要数据库辅助&#xff09; —> 生成测试报…

linux环境执行jar脚本

一、前言 平常工作中&#xff0c;我们的开发的项目部署到linux环境&#xff0c;以jar包的方式运行&#xff0c;涉及jar包的启动、停止、查看状态等&#xff0c;我们可以通过脚本的方式进行维护&#xff0c;减少自己敲打一长串的命令少敲一个字母或者多敲一个字母&#xff0c;方…

实现寄生组合继承

寄生组合继承是一种继承方式&#xff0c;它通过组合使用构造函数继承和原型继承的方式&#xff0c;实现了高效而且正确的继承方式。 具体实现步骤如下&#xff1a; ① 定义一个父类&#xff0c;实现其属性和方法&#xff1a; function Person(name) {this.name namethis.age…

浙大陈越何钦铭数据结构06-图1 列出连通集

题目 给定一个有N个顶点和E条边的无向图&#xff0c;请用DFS和BFS分别列出其所有的连通集。假设顶点从0到N−1编号。进行搜索时&#xff0c;假设我们总是从编号最小的顶点出发&#xff0c;按编号递增的顺序访问邻接点。 输入格式: 输入第1行给出2个整数N(0<N≤10)和E&…

Docker多平台、跨平台编译打包

大多数带有Docker官方标识的镜像都提供了多架构支持。如&#xff1a;busybox镜像支持amd64, arm32v5, arm32v6, arm32v7, arm64v8, i386, ppc64le, and s390x。当你在amd64设备上运行容器时&#xff0c;会拉取amd64镜像。 当你需要构建多平台镜像时&#xff0c;可以用 --platf…

python操作MySQL、SQL注入问题、视图、触发器、事务、存储过程、函数、流程控制、索引(重点)

python操作MySQL(重要) SQL的由来&#xff1a; MySQL本身就是一款C/S架构&#xff0c;有服务端、有客户端&#xff0c;自身带了有客户端&#xff1a;mysql.exe python这门语言成为了MySQL的客户端(对于一个服务端来说&#xff0c;客户端可以有很多) 操作步骤&#xff1a; …

Kafka - 深入了解Kafka基础架构:Kafka的基本概念

文章目录 Kafka的基本概念 Kafka的基本概念 我们首先了解一些Kafka的基本概念。 1&#xff09;Producer &#xff1a;消息生产者&#xff0c;就是向kafka broker发消息的客户端2&#xff09;Consumer &#xff1a;消息消费者&#xff0c;向kafka broker获取消息的客户端3&…

二、【常用的几种抠图方式一】

文章目录 选框抠图快速选择工具抠图魔棒工具抠图对象选择工具抠图套索工具抠图多边形套索工具抠图磁性套索工具抠图 选框抠图 选框工具抠图适合规则的图形&#xff0c;如下图先使用选框工具框出对象的图轮廓&#xff0c;然后再选择并遮住在里边擦出图形的边缘&#xff0c;根据…

Spring MVC 执行流程

前言 Spring MVC 是一个非常强大的框架&#xff0c;它能够帮助开发人员快速构建高效的 Web 应用程序。然而&#xff0c;要理解 Spring MVC 的执行流程并不容易&#xff0c;因为它涉及到多个组件和模块。在本文中&#xff0c;我们将介绍 Spring MVC 的执行流程&#xff0c;帮助…

Vue缓存策略:提升应用性能与用户体验

&#x1f31f; Vue缓存策略&#xff1a;提升应用性能与用户体验 &#x1f4aa; 在Vue.js应用程序开发中&#xff0c;缓存是一种常见的优化策略&#xff0c;它可以有效提升应用的性能和用户体验。Vue提供了一些灵活且易于使用的缓存机制&#xff0c;可以帮助我们优化组件的渲染…

springboot移除nacos yml无法加载

springboot2的一个项目移除了nacos&#xff0c;一直起不来。最后发现是配置文件名称没有修改导致的。 需要把bootstrap.yml改成application.yml bootstrap.yml 和 application.yml 都是Spring Boot应用程序的配置文件&#xff0c;但它们有不同的加载顺序和作用&#xff1a; 加…

Java IDEA feign调用上传文件MultipartFile以及实体对象亲测可行

Java IDEA feign调用上传文件MultipartFile以及实体对象亲测可行 1. 报错 java.lang.IllegalStateException: Body parameter cannot be used with form parameters2. 解决参考 1. 报错 java.lang.IllegalStateException: Body parameter cannot be used with form parameters …

uniapp vue国际化 i18n

一、安装 vue-i18n npm i vue-i18n 二、新建i18n目录 1、en.json 内容 {"loginPage":{"namePh":"Please enter your login account","passwordPh":"Please enter password"} } 2、zh-CN.json 内容 {"loginPage&qu…

CentOS卸载LVM磁盘的方法

在客户环境上遇到一个问题&#xff0c;本身的磁盘满了&#xff0c;需要把没有用的lvm逻辑卷卸载掉&#xff0c;然后挂上去&#xff0c;下面记录一下过程。 卸载原磁盘 umount /data # 如果/data目录正在被其他进程使用中&#xff0c;则使用fuser强制关闭&#xff0c;然后Umou…

Android开发中关于Ui的语法糖

一、layout_margin和padding android:layout_margin“10dp”&#xff0c;android:padding"10dp"区别 android:layout_margin"10dp"&#xff1a; 适用对象&#xff1a;用于调整 View 与其父容器或相邻 View 之间的距离&#xff0c;即外边距&#xff08;mar…

MacOS Mojave(苹果14系统) v10.14.6中文离线安装包

MacOS Mojave是一款先进的操作系统&#xff0c;它拥有诸多出色的特性。其中&#xff0c;夜间模式可以根据时间或用户设置自动切换&#xff0c;改变了UI、壁纸和窗口的样式&#xff0c;使界面在夜晚使用时更为舒适。另外&#xff0c;新的堆栈和群组功能让用户能更方便地分类和整…

7 IT Career Paths and How to Get Started in 2023

文章目录 7 IT Career Paths and How to Get Started in 2023IT career paths1. Computer support2. Cybersecurity3. Networks and systems4. Software development5. Web development6. Data7. Cloud computing 7 IT Career Paths and How to Get Started in 2023 Working i…

Tomcat的日志接收文件catalina.out nohup.out说明

catalina.out用于接收如下情况的日志&#xff1a; catalina.out其实是tomcat的标准输出(stdout)和标准出错(stderr)&#xff0c;这是在tomcat的启动脚本里指定的&#xff0c;如果没有修改的话stdout和stderr会重定向到这里。所以我们在应用里使用System.out打印的东西都会到这…

软考系统架构师知识点集锦四:信息安全技术基础知识

一、考情分析 二、考点精讲 2.1信息加解密技术 2.1.1对称加密 概念:对称加密(又称为私人密钥加密/共享密钥加密) : 加密与解密使用同一密钥。特点:加密强度不高&#xff0c;但效率高;密钥分发困难。 (大量明文为了保证加密效率一般使用对称加密) 常见对称密钥加密算法:DES:…