作者:張富春(ahfuzhang),轉載時請注明作者和參考鏈接,謝謝!
- cnblogs博客
- zhihu
- Github
- 公眾號:一本正經的瞎扯
為了提升性能,使用 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
選項,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/555048.html
標籤:Go
上一篇:Go 語言之 sqlx 庫使用
下一篇:返回列表