go binary包

news2025/2/19 23:31:05

binary包使用与详解

最近在看一个第三方包的库源码,bigcache,发现其中用到了binary 里面的函数,所以准备研究一下。
可以看到binary 包位于encoding/binary,也就是表示这个包的作用是编辑码作用的,看到文档给出的解释是
用于数字和字节序的转换以及变长值的编解码。

Uvarint

从buf解码一个uint64,返回该数字和读取的字节长度,如果发生了错误,该数字为0而读取长度n返回值的意思是:

n == 0 buf 太小了,没读到
n < 0 值太大了,64 bit 装不下,-n 为可以读到的字节数

看以下这个函数,我们不论这个函数是干啥的,我们只用关注

blockSize, n := binary.Uvarint(q.array[index:])
return q.array[index+n : index+int(blockSize)], int(blockSize), nil
从array 中index 的位置之后解析一个unit64, blockSize 为这个值,n 表示读到的字节数
什么意思呢?
比方说我设置了一个package 的一个大小,500byte,我把这个500这个数字编码到byte buf中,这个时候我们通过binary.Uvarint 这个解码出来的有两个值,按照这个方法,blockSize 这个就是500,n 表示编码这个500 占的字节数
那么通过这个peek 方法,实际上我们返回的值就是,从array这个byte buf中取从index+这个编码 之后到packeage的大小字符。说白了就是packege的数据。

// peek returns the data from index and the number of bytes to encode the length of the data in uvarint format
func (q *BytesQueue) peek(index int) ([]byte, int, error) {
	err := q.peekCheckErr(index)
	if err != nil {
		return nil, 0, err
	}

	blockSize, n := binary.Uvarint(q.array[index:])
	return q.array[index+n : index+int(blockSize)], int(blockSize), nil
}

PutUvarint

同样的,我们看下面的函数,我们不用考虑这个函数具体作用,只用分析

headerEntrySize := binary.PutUvarint(q.headerBuffer, uint64(len))
PutUvarint 编码,将什么编码呢,将这个uint64的长度这个数字进行编码,最终放在了q.headerBuffer这个[]byte中,返回的headerEntrySize 这值是什么呢,这个值就是编码用了多少字节。

func (q *BytesQueue) push(data []byte, len int) {
	headerEntrySize := binary.PutUvarint(q.headerBuffer, uint64(len))
	q.copy(q.headerBuffer, headerEntrySize)

	q.copy(data, len-headerEntrySize)

	if q.tail > q.head {
		q.rightMargin = q.tail
	}
	if q.tail == q.head {
		q.full = true
	}

	q.count++
}

PutUint64

同样的,我们不去管这个函数做了什么(实际上也很好理解),我们具体看

binary.LittleEndian.PutUint64(blob, timestamp)
binary.LittleEndian.PutUint64(blob[timestampSizeInBytes:], hash)
binary.LittleEndian.PutUint16(blob[timestampSizeInBytes+hashSizeInBytes:], uint16(keyLength))
事实上这个就是把timestamp hash keyLength的长度,这三个数字,给他放到blob 这个[]byte中,很明显这三个占了18字节

func wrapEntry(timestamp uint64, hash uint64, key string, entry []byte, buffer *[]byte) []byte {
	keyLength := len(key)
	blobLength := len(entry) + headersSizeInBytes + keyLength

	if blobLength > len(*buffer) {
		*buffer = make([]byte, blobLength)
	}
	blob := *buffer

	binary.LittleEndian.PutUint64(blob, timestamp)
	binary.LittleEndian.PutUint64(blob[timestampSizeInBytes:], hash)
	binary.LittleEndian.PutUint16(blob[timestampSizeInBytes+hashSizeInBytes:], uint16(keyLength))
	copy(blob[headersSizeInBytes:], key)
	copy(blob[headersSizeInBytes+keyLength:], entry)

	return blob[:blobLength]
}

Uint64

同样的,我们选择其中一个readEntry 的函数看看,是如何解码的,很简单

length := binary.LittleEndian.Uint16(data[timestampSizeInBytes+hashSizeInBytes:])
length就是我们前面设置的key 的长度

func readEntry(data []byte) []byte {
	length := binary.LittleEndian.Uint16(data[timestampSizeInBytes+hashSizeInBytes:])

	// copy on read
	dst := make([]byte, len(data)-int(headersSizeInBytes+length))
	copy(dst, data[headersSizeInBytes+length:])

	return dst
}

封装一个[]byte

考虑一下,我们的网络通信过程中,假设我们需要进行封包的操作。
在这里插入图片描述
假设我么利用上述的方法,对自己的协议进行自定义。
我们看看应用如何实现

func TestCmd(t *testing.T) {
	b := "hello"
	bb := []byte(b)
	encodeB := encode(ICmd{id: 1,cmd: 2,pLen: 5,message: bb})
	fmt.Println(string(encodeB))
	icmd := decode(encodeB)
	fmt.Println(icmd.id)
	fmt.Println(icmd.cmd)
	fmt.Println(icmd.pLen)
	fmt.Println(string(icmd.message))
}
type ICmd struct {
	id uint32
	cmd uint32
	pLen uint32
	message []byte
}
//然后我们在进行encode 和 decode 操作
const (
	idFixSize = 4
	cmdFixSize = 4
	pLenFixSize = 4
	SumIdCmdPLenSize = idFixSize+cmdFixSize+pLenFixSize
)
func encode(icmd ICmd) []byte {
	newBuf := make([]byte,len(icmd.message)+SumIdCmdPLenSize)
	binary.LittleEndian.PutUint32(newBuf,icmd.id)
	binary.LittleEndian.PutUint32(newBuf[cmdFixSize:],icmd.cmd)
	binary.LittleEndian.PutUint32(newBuf[idFixSize+cmdFixSize:],icmd.pLen)
	copy(newBuf[SumIdCmdPLenSize:],icmd.message)
	return newBuf
}

func decode(data []byte) ICmd{
	icmd := ICmd{}
	icmd.id = binary.LittleEndian.Uint32(data[:idFixSize])
	icmd.cmd = binary.LittleEndian.Uint32(data[idFixSize:idFixSize+cmdFixSize])
	icmd.pLen = binary.LittleEndian.Uint32(data[idFixSize+cmdFixSize:SumIdCmdPLenSize])
	icmd.message = data[SumIdCmdPLenSize:]
	return icmd
}

在这里插入图片描述

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

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

相关文章

加密的本质:数学的不对称性

文章目录 引言I 预备知识1.1 加密和授权1.2 非对称的特性II 椭圆曲线加密的方法2.1 椭圆曲线2.2 椭圆曲线的性质引言 不对称有时却自有其妙处与美感,比如黄金分割就是不对称的。 可以通过加密和授权,兼顾保护信息不外泄,而且某些得到授权的人还能使用信息。 I 预备知识 …

2022年人民满意手机银行发展洞察

易观&#xff1a;商业银行积极践行“金融为民”&#xff0c;坚持“以用户为中心”的发展理念&#xff0c;从全客群、全服务、全渠道推动金融服务触达广大人民群众。其中&#xff0c;手机银行作为服务及经营主阵地&#xff0c;是人民群众获取金融服务的超级入口及服务平台。 “以…

键盘摄影:今天老李是一名动物摄影师

键摄&#xff0c;全称键盘摄影师。原本是一个贬义词&#xff0c;是指那些没有相机&#xff0c;没有实拍经验&#xff0c;仅凭一副鼠标键盘&#xff0c;在家里打字&#xff0c;在网上头头是道地分享摄影技巧&#xff0c;同时对别人的作品指指点点&#xff0c;然后又无法秀出自己…

中国上海人工智能企业中集飞瞳全球港航AI独角兽企业,成熟智慧港航AI产品,数字化港口自动化码头智慧港航智能化中国上海人工智能企业

中国上海人工智能企业中集飞瞳全球港航AI独角兽企业&#xff0c;成熟智慧港航AI产品&#xff0c;数字化港口自动化码头智慧港航。CIMCAI打造全球领先新一代集装箱管理方案&#xff0c;手机小程序随时随地AI验箱&#xff0c;自动化箱况检测信息识别&#xff0c;集装箱信息识别箱…

总线的控制

总线控制 目录总线控制总线的判优控制链式查询计数器定时查询独立请求小结总线通信控制同步通信异步通信半同步通信分离通信由于总线上连接着多个部件&#xff0c;什么时候由哪个部件发送信息&#xff0c;如何给传送信息定时&#xff0c;如何防止信息丢失&#xff0c;如何避免多…

React源码解析之createElement和render方法

参考资料 请注意&#xff0c;这是React16.8的源码解析&#xff0c;当然他完全可以作为你阅读源码的参考&#xff0c;他还没有落后。 Step1 开始之前&#xff0c;要先了解一个知识点⬇️ 我们都知道&#xff0c;要在JSX中写React语法&#xff0c;那为什么不能在js文件中写呢&am…

微搭使用笔记(六) 通过源码组件实现小程序端地图

前言 微搭官方提供了大量常用组件&#xff0c;但由于微搭本身也是在不断地完善过程中&#xff0c;有些组件还是没有提供&#xff0c;但同时微搭允许用户自定义组件并在应用中使用。 实际场景是这样的&#xff0c;我们需要一个地图页面在上面展示已知设备的信息和位置&#xf…

CentOS7 虚拟机 双网卡绑定

一、网卡绑定模式 模式类型特点mode0round-robin&#xff08;平衡轮询策略&#xff09;基于per packet方式&#xff0c;轮询往每条链路发送报文。提供负载均衡和容错的能力&#xff0c;当有链路出问题&#xff0c;会把流量切换到正常的链路上。交换机端需要配置聚合口。mode1a…

Linux网络编程(四)——UDP通信

目录 0x01 UDP协议 一、UDP通信简介以及接口 二、UDP的接口 三、UDP收发例程 0x02 广播 一、设置广播数据函数接口 二、广播代码实现 0x03 组播&#xff08;多播&#xff09; 一、组播地址 二、设置组播函数接口 三、代码实现 0x01 UDP协议 一、UDP通信简介以及接口…

《Netty》从零开始学netty源码(三十六)之ChannelConfig

ChannelConfig 在前面创建NioServerSocketChannel的 构造函数中&#xff0c;最后一步创建了channel属性的配置类NioServerSocketChannelConfig&#xff0c;本文详细分析下该类&#xff0c;先看下其类结构图。 类结构图 服务端使用的NioServerSocketChannelConfig&#xff0c;…

ELK日志分析系统+zookeeper

ELK日志分析系统zookeeper一、zookeeper简介1、zookeeper概念2、zookeeper数据结构二、zookeeper工作机制1、zookeeper特点2、zookeeper应用场景三、zookeeper集群部署1、安装前先关闭防火墙 核心防护2、安装JDK3、安装zookeeper4、修改配置文件5、创建数据目录和日志目录&…

JavaEE-网络原理之UDP协议

目录UDP报文结构UDP的特点无连接不可靠面向数据报缓冲区基于UDP的应用层协议UDP报文结构 报头大小为8个字节. 16位源端口号与16位目的端口号: 16个比特位可表示65536个端口号,分别为0-65535,其中1-1023为为专属端口号,用来为一些知名服务器提供服务,例如: HTTP服务器专属端口号…

【CSS】课程网站 网格商品展示 模块制作 ③ ( 清除浮动需求 | 没有设置高度的盒子且内部设置了浮动 | 使用双伪元素清除浮动 )

文章目录一、清除浮动需求 ( 没有设置高度的盒子且内部设置了浮动 )二、清除浮动代码示例一、清除浮动需求 ( 没有设置高度的盒子且内部设置了浮动 ) 如果盒子没有设置高度 , 并且盒子中还设置了浮动 , 如上一篇博客 【CSS】课程网站 网格商品展示 模块制作 ② ( 网格商品展示盒…

溯源反制(windows)

痛点&#xff1a; windows服务器被恶意入侵出现遭受挖矿&#xff0c;在没有专业的安全溯源反制的工具如何排查系统异常文件精准找出异常程序呢&#xff1f; 这样的吗 使用开源火绒、后门工具、D_盾_web查杀工具、360安全卫士进行全局查杀搜寻异常文件程序。 在应急响应中&am…

SpringBoot默认包扫描机制与默认配置文件

文章目录一、SpringBoot默认包扫描机制 - 示例二、SpringBoot默认扫描包机制 - 原理三、SpringBoot手动扫描包机制 - 原理&示例四、ComponentScan与MapperScan五、SpringBoot默认配置文件一、SpringBoot默认包扫描机制 - 示例 默认情况下&#xff0c;扫描启动类同级及其子…

护眼灯到底有没有用?盘点口碑销量好的护眼台灯

有一定的护眼作用。很多人认为护眼灯是智商税&#xff0c;想法并不正确&#xff0c;市面上品质好的护眼灯&#xff0c;光线是通过特殊处理过的&#xff0c;色温、亮度、光线均匀度、显色性都贴合人眼&#xff0c;并且能够护眼的效果。 我们在挑选护眼灯时&#xff0c;在室内环…

【MyBatis Plus】003 -- 配置(基本、进阶、DB策略) 条件构造器

目录 4、配置 4.1 基本配置 4.1.1 configLocation &#xff08;MyBatis 配置文件位置&#xff09; 4.1.2 mapperLocations&#xff08;MyBatis Mapper 所对应的 XML 文件位置&#xff09; 4.1.3 typeAliasesPackage &#xff08;别名包扫描路径&#xff09; 4.2 进阶配置 4.2.1…

代码随想录算法训练营第五十七天 | 647. 回文子串、516. 最长回文子序列、动态规划总结

647. 回文子串 动规五部曲 1、确定dp数组&#xff08;dp table&#xff09;以及下标的含义 在判断字符串S是否为回文时&#xff0c;如果知道 s[1]&#xff0c;s[2]&#xff0c;s[3] 这个子串是回文的&#xff0c;那么只需要比较 s[0]和s[4]这两个元素是否相同&#xff0c;如果…

NodeJS Cluster模块基础教程

Cluster简介 默认情况下&#xff0c;Node.js不会利用所有的CPU&#xff0c;即使机器有多个CPU。一旦这个进程崩掉&#xff0c;那么整个 web 服务就崩掉了。 应用部署到多核服务器时&#xff0c;为了充分利用多核 CPU 资源一般启动多个 NodeJS 进程提供服务&#xff0c;这时就…

【java】面向对象的编程基础

文章目录面向对象的编程基础定义方法重载执行顺序静态变量和方法加载顺序包和访问控制类的封装object类方法重写抽象类接口枚举类面向对象的编程基础 定义 public class person { String name; int age; char sex; person(String name,int age,char sex) {this.ageage;this.…