追記: go1.9 でこの方法を使うと安定動作しない可能性があります
- golang で string を []byte にキャストするとメモリコピーが走ります - Qiita
- メモ:golang で []byte と string の読み込みを透過的に扱う試行錯誤 - Qiita
コピーしない方法は無くはないんじゃないかなーと思いまして筆を取りました。
golang では []byte(str)
をコンパイルすると runtime.go
の stringtoslicebyte
によりバイト配列にコピーされます。
runtime.go
func stringtoslicebyte(s string) []byte {
b := rawbyteslice(len(s))
copy(b, s)
return b
}
しかし実際に golang は内部で utf-8 な文字列を扱っている訳なので、そのまま取り出す方法さえあればいいはずなんですよね。
そこで unsafe
の登場です...
これを使うと自らプログラムを破壊出来るんだ。
内部のバイト列はもちろん...
stringbytes.go
package main
import (
"fmt"
"unsafe"
)
func main() {
str := "hello, world!"
vec := *(*[]byte)(unsafe.Pointer(&str))
fmt.Println(vec)
}
ポインタ演算まで出来ちゃう!
pointer.go
package main
import (
"unsafe"
)
type foo struct {
k int64
v int64
}
func main() {
f := &foo{3,4}
// unsafe.Pointer() で匿名ポインタにして
// uintptr() で演算可能にして
// +8 バイト(64bit)足して
// unsafe.Pointer で匿名ポインタに戻して
// そこにはフィールド v があるはずなので *int64 にキャストして
// デリファレンスすれば出来上がり
*(*int64)(unsafe.Pointer((uintptr(unsafe.Pointer(f))+8))) = 5 // グヒヒ
println(f.v) // 5
}
ちなみに上記の stringbytes.go
を go tool 8g -S stringbytes.go
でアセンブラにしてみると...
0x001c 00028 (stringbytes2.go:8) LEAL go.string."hello, world!"+0(SB),BX
0x0022 00034 (stringbytes2.go:8) MOVL (BX),CX
0x0024 00036 (stringbytes2.go:8) MOVL 4(BX),BP
0x0027 00039 (stringbytes2.go:9) MOVL CX,"".str+24(SP)
0x002b 00043 (stringbytes2.go:9) MOVL CX,(SP)
0x002e 00046 (stringbytes2.go:9) MOVL BP,"".str+28(SP)
0x0032 00050 (stringbytes2.go:9) MOVL BP,4(SP)
0x0036 00054 (stringbytes2.go:9) PCDATA $0,$0
0x0036 00054 (stringbytes2.go:9) CALL ,runtime.stringtoslicebyte(SB)
0x003b 00059 (stringbytes2.go:9) MOVL 8(SP),DX
0x003f 00063 (stringbytes2.go:9) MOVL 12(SP),CX
0x0043 00067 (stringbytes2.go:9) MOVL 16(SP),AX
※部分抜粋
この様にコードに落ちていたのが
0x001c 00028 (stringbytes.go:9) LEAL go.string."hello, world!"+0(SB),BX
0x0022 00034 (stringbytes.go:9) MOVL (BX),BP
0x0024 00036 (stringbytes.go:9) MOVL BP,"".str+24(SP)
0x0028 00040 (stringbytes.go:9) MOVL 4(BX),BP
0x002b 00043 (stringbytes.go:9) MOVL BP,"".str+28(SP)
0x002f 00047 (stringbytes.go:10) LEAL "".str+24(SP),BX
0x0033 00051 (stringbytes.go:10) CMPL BX,$0
0x0036 00054 (stringbytes.go:10) JEQ $1,232
0x003c 00060 (stringbytes.go:10) MOVL (BX),DX
0x003e 00062 (stringbytes.go:10) MOVL 4(BX),CX
0x0041 00065 (stringbytes.go:10) MOVL 8(BX),BP
こーんな形になるー!
これでコピー無しで文字列が扱える!
プログラムも高速になる!
だれも困らない!
みんなハッピー!
みんなどんどん使っていこう!
unsafe をご使用になる時は、用法・用量を守り正しくお使い下さい。race conditionでもあり、あらゆるケースにおいてもGC-safeという訳ではありません。