【解决一个小问题】golang 的 `-race`选项导致 unsafe代码 panic
作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢!
为了提升性能,使用 unsafe 代码来重构了凯撒加密的代码。代码如下:
const (
lowerCaseAlphabet = "abcdefghijklmnopqrstuvwxyz"
upperCaseAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
)
var (
lowerCaseAlphabetArr = []byte(lowerCaseAlphabet)
upperCaseAlphabetArr = []byte(upperCaseAlphabet)
tableOflowerCaseAlphabet = unsafe.Pointer(&lowerCaseAlphabetArr[0])
tableOfupperCaseAlphabe = unsafe.Pointer(&upperCaseAlphabetArr[0])
)
// CaesarFastEncode fast version
func CaesarFastEncode(in []byte, out []byte, rot int) {
start := unsafe.Pointer(&in[0])
target := unsafe.Pointer(&out[0])
for i := 0; i < len(in); i++ {
c := *((*byte)(start))
if c == '.' {
*((*byte)(target)) = '='
} else if c >= 'a' && c <= 'z' {
idx := (int(26+(c-'a')) + rot) % 26
*((*byte)(target)) = *((*byte)(unsafe.Pointer(uintptr(tableOflowerCaseAlphabet) + uintptr(idx))))
} else if c >= 'A' && c <= 'Z' {
idx := (int(26+(c-'A')) + rot) % 26
*((*byte)(target)) = *((*byte)(unsafe.Pointer(uintptr(tableOfupperCaseAlphabe) + uintptr(idx))))
} else {
*((*byte)(target)) = *((*byte)(start))
}
start = unsafe.Pointer(uintptr(start) + uintptr(1))
target = unsafe.Pointer(uintptr(target) + uintptr(1))
}
}
命令行运行go test 的时候发现,代码中发生了 panic。而我直接在 vscode 中通过快捷键运行又是正常的。
错误信息如下:
fatal error: checkptr: pointer arithmetic result points to invalid allocation
goroutine 6 [running]:
runtime.throw({0x104936d70?, 0x10410270c?})
/opt/homebrew/Cellar/go/1.20.4/libexec/src/runtime/panic.go:1047 +0x40 fp=0xc0001e5a60 sp=0xc0001e5a30 pc=0x104132280
runtime.checkptrArithmetic(0xc0001e5ac8?, {0xc0001e5b00, 0x1, 0x0?})
/opt/homebrew/Cellar/go/1.20.4/libexec/src/runtime/checkptr.go:69 +0xac fp=0xc0001e5a90 sp=0xc0001e5a60 pc=0x1041028cc
cryptoutil.CaesarFastEncode({0xc000216cc4, 0xc, 0xc0001e5b68?}, {0xc000232a02, 0x1fe, 0x104f3ee00?}, 0x3)
/Users/ahfuzhang/code/golang/xxx/caesar.go:83 +0x84 fp=0xc0001e5b20 sp=0xc0001e5a90 pc=0x104570a74
进一步发现,命令行中有个不一样的选项:
go test -v -cover -race ./...
去掉 -race选项后,一切正常。
搜索看到了这篇文章:《Go 1.15中值得关注的几个变化》
Go 1.14版本中,Go编译器在被传入-race和-msan的情况下,默认会执行-d=checkptr,即对unsafe.Pointer的使用进行合法性检查。-d=checkptr主要检查两项内容:
•当将unsafe.Pointer转型为*T时,T的内存对齐系数不能高于原地址的;
•做完指针算术后,转换后的unsafe.Pointer仍应指向原先Go堆对象
由此可见,循环做完后,最后一行必然导致指针超出原来的 buffer。
为了符合 golang 的规范,微调了代码后通过:
// CaesarFastEncode fast version
func CaesarFastEncode(in []byte, out []byte, rot int) {
start := unsafe.Pointer(&in[0])
end := uintptr(start) + uintptr(len(in)-1)
target := (unsafe.Pointer(&out[0]))
for {
c := *((*byte)(start))
if c == '.' {
*((*byte)(target)) = '='
} else if c >= 'a' && c <= 'z' {
idx := (int(26+(c-'a')) + rot) % 26
*((*byte)(target)) = *((*byte)(unsafe.Pointer(uintptr(tableOflowerCaseAlphabet) + uintptr(idx))))
} else if c >= 'A' && c <= 'Z' {
idx := (int(26+(c-'A')) + rot) % 26
*((*byte)(target)) = *((*byte)(unsafe.Pointer(uintptr(tableOfupperCaseAlphabe) + uintptr(idx))))
} else {
*((*byte)(target)) = *((*byte)(unsafe.Pointer(start)))
}
if uintptr(start) >= end {
break
}
start = unsafe.Pointer(uintptr(start) + uintptr(1))
target = unsafe.Pointer(uintptr(target) + uintptr(1))
}
}
由此看来,只要使用了 unsafe 代码,都应该加上-race选项。
【解决一个小问题】golang 的 `-race`选项导致 unsafe代码 panic的更多相关文章
- 解决 VS Code 中 golang.org 被墙导致的 Go 插件安装失败问题
微软官方开发的 Go for Visual Studio Code 插件为 Go 语言 提供了丰富的支持.在 VS Code 中首次打开 Go 工作区后,VS Code 会自动检测当前开发环境为 Go ...
- 使用docker 解决一个小问题,你也可能用的到
以前一直觉得docker是运维用的工具,或者devops 用的工具,一般人应该用不上,直到最近发现docker 还有另外一个妙用,不管是什么语言. 这几天开会网络特别不好,nodejs npm 仓库 ...
- eclipse_neon 的Spket 目录下只有一个Task Tags,没有其他的选项,导致没有办法添加提示文件! 添加sdk文件之后还是没有办法显示的解决办法
问题解决办法: 将 spket-1.6.23的安装包里面的features plugins 单独复制到D:\eclipse_neon\dropins 目录下,重启一下eclipse即可正常显示! 添 ...
- 如果公司里有上百个表要做触发器,如果手动写代码的话。很累,所以今天写了一个小程序,自动生成mysql的触发代码。
<?php $dbname = 'test';//数据库 $tab1 = 'user'; //执行的表 $tab2 = 'user_bak'; //被触发的表 $conn = mysql_con ...
- 关于反射的一个小问题---.NetFrameWork版本不一样导致不同的系统的问题
背景: 近期项目中用到发射,本人的电脑上是安装了.NetFrameWork 4.5,然后用着发射蛮顺溜的,啪啪,三下五除二,项目完成了,然后提交测试了,测试的电脑是虚拟机上安装了xp系统,然后.Net ...
- go的变量redeclare的问题,golang的一个小坑
go的变量声明有几种方式: 1 通过关键字 var 进行声明 例如:var i int 然后进行赋值操作 i = 5 2 最简单的,通过符号 := 进行声明和赋值 例如: i:=5 golang会 ...
- linux下开发,解决cocos2d-x中编译出现的一个小问题, undefined reference to symbol 'pthread_create@@GLIBC_2.2.5'
解决cocos2d-x中编译出现的一个小问题 对于cocos2d-x 2.×中编译中,若头文件里引入了#include "cocos-ext.h",在进行C++编译的时候会遇到例如 ...
- golang中的选项模式
索引 https://waterflow.link/articles/1663835071801 当我在使用go-zero时,我看到了好多像下面这样的代码: ... type ( // RunOpti ...
- 与大家分享robotium一个小问题。Test run failed:Instrumentation run failed due to 'java.lang.ClassNotFoundException'
今天和大家分享robotium一个小问题. 我们在运行自已经搭好的框架时,有可能会出现一个找不到类的错误(如上图所示). 问题是重签名工具给出的activity有误,这时我们可以用Appt命令查看重签 ...
- 用struts2标签如何从数据库获取数据并在查询页面显示。最近做一个小项目,需要用到struts2标签从数据库查询数据,并且用迭代器iterator标签在查询页面显示,可是一开始,怎么也获取不到数据,想了许久,最后发现,是自己少定义了一个变量,也就是var变量。
最近做一个小项目,需要用到struts2标签从数据库查询数据,并且用迭代器iterator标签在查询页面显示,可是一开始,怎么也获取不到数据,想了许久,最后发现,是自己少定义了一个变量,也就是var变 ...
随机推荐
- Go语言逆向技术:常量字符串
摘要:Go语言源代码编译成二进制文件后,源代码中的字符串存放在哪里?是如何组织的? 本文分享自华为云社区<go语言逆向技术之---常量字符串解密>,作者:安全技术猿. Go语言源代码编译成 ...
- Hive 报错 FAILED: SemanticException [Error 10096]: Dynamic partition strict mode requires at least one static partition column. To turn this off set hive.exec.dynamic.partition.mode=nonstrict —————
hive中设置 set hive.exec.dynamici.partition=true; set hive.exec.dynamic.partition.mode=nonstrict;
- 在低代码开发平台 ILLA Cloud 中使用 Hugging Face 上的模型
ILLA Cloud 是一个面向开发者的开源低代码开发平台,平台专注于帮助开发者快速建立企业内部应用,为开发者节约数据调用与页面设计的时间.平台具有面向开发者.数据整合.协同开发.灵活部署等功能与特点 ...
- 一次惨痛教训让我写了个Windows定期备份文件脚本
前言 说实话在写这篇文章的时候,咸鱼不禁又想起了那件男默女泪的往事 我喜欢做笔记,我觉得好记性不如烂笔头,所以在我的学生以及职业生涯阶段,我用过四款笔记应用--Onenote.语雀.印象笔记.Ty ...
- Codeforces Round #619 (Div. 2) A-D
比赛链接:Here 1301A. Three Strings 题意: 给三个相同长的字符串 \(a,b,c\) ,对于每个位置 \(i\) ,你必须做一次操作:交换 \(a_i\) 和 \(c_ ...
- Java | VS Code 如何导入外部 jar 包源
Java Extension Pack里的Project Manager for Java扩展,安装后在文档查看的下方会有"JAVA PROJECTS",在Referenced L ...
- 1 分钟在 Serverless 上部署现代化 Deno Web 应用
作者 | 连喆人(掌上乾坤公司) 本文选自 "Serverless 函数计算征集令" 征文 利用 Serverless 的水平扩展与按量付费优势, 结合自定义运行时, 实现 Web ...
- centos7.9 安装oracle11g
安装环境: 操作系统:CentOS Linux release 7.9.2009 (Core)orcle安装包:linux.x64_11gR2_database_1of2.zip. linux.x64 ...
- 5 Englishi 词根
词根 1 ced/cess = go 行走 precede pre=before ced =go unprecedentedly un pre ced +ed变成adj +ly 变 ...
- vue表单修饰符
