Help us understand the problem. What is going on with this article?

golang で string を []byte にキャストしてもメモリコピーが走らない方法を考えてみる

More than 1 year has passed since last update.

追記: go1.9 でこの方法を使うと安定動作しない可能性があります

コピーしない方法は無くはないんじゃないかなーと思いまして筆を取りました。

golang では []byte(str) をコンパイルすると runtime.gostringtoslicebyte によりバイト配列にコピーされます。

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.gogo 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という訳ではありません。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした