2013-07-09 13 views
8

Używam big.NewInt (int64 (e)) .Bytes() do konwersji tablicy int32 na tablicę bajtową. Czy istnieje bardziej elegancki sposób na zrobienie tego?Konwertowanie tablicy int32 na bajt w go

Spodziewam Aqab się być zakodowane base64 wartość e

http://play.golang.org/p/M46X7OpZpu

const e = 65537 

func base64Encode(b []byte) string { 
    return strings.TrimRight(base64.StdEncoding.EncodeToString(b), "=") 
} 

func main() { 
    fmt.Printf("exp %d\n", e) 

    b := make([]byte, 4) 
    binary.BigEndian.PutUint32(b, e) 
    fmt.Printf("b: BigEndian.PutUint32 %x (Bad) %s\n", b, base64Encode(b)) 

    b2 := make([]byte, 4) 
    binary.BigEndian.PutUint32(b2, e) 
    for i := range b2 { 
    if b2[i] != 0 { 
    b2 = b2[i:] 
    break 
    } 
    } 
    fmt.Printf("b2: BigEndian.PutUint32 %x (Good) %s\n", b2, base64Encode(b2)) 

    b4 := big.NewInt(int64(e)).Bytes() 
    fmt.Printf("b4: big.NewInt(int64(e)).Bytes() %x (Good) %s\n", b4, base64Encode(b4)) 
} 

wyjściowa:

exp 65537 
b: BigEndian.PutUint32 00010001 (Bad) AAEAAQ 
b2: BigEndian.PutUint32 010001 (Good) AQAB 
b4: big.NewInt(int64(e)).Bytes() 010001 (Good) AQAB 

exp 1 
b: BigEndian.PutUint32 00000001 (Bad) AAAAAQ 
b2: BigEndian.PutUint32 01 (Good) AQ 
b4: big.NewInt(int64(e)).Bytes() 01 (Good) AQ 

exp 1000000 
b: BigEndian.PutUint32 000f4240 (Bad) AA9CQA 
b2: BigEndian.PutUint32 0f4240 (Good) D0JA 
b4: big.NewInt(int64(e)).Bytes() 0f4240 (Good) D0JA 

Edit:

Mam porównywana b2 i b4:

b2 1000000000   68.1 ns/op   8 B/op   1 allocs/op 
b4 200000000   248 ns/op   90 B/op   3 allocs/op 

użyję b2 teraz ...

Odpowiedz

5

Dla tego rodzaju zadania, myślę, że pierwsze opcje powinny być zawsze przy użyciu encoding/binary i, jeśli jest to niewystarczające, bitowe matematyki. Jednak w niektórych przypadkach narzut kopiowania danych jest zbyt duża lub te bezpieczne rozwiązania są zbyt powolne:

Choć nie nazwałbym to elegancki można użyć GO unsafe i reflect * pakiety to zrobić bardzo szybko. Pamiętaj tylko, że nie kopiuje danych; raczej po prostu daje inny "widok" tego. A bycie dobrze-niebezpiecznym oznacza, że ​​musisz być bardzo ostrożny (cześć testy jednostek i przegląd kodu) i pamiętaj, że łamiesz Go memory safety. Jednak, gdy dominującą kwestią jest szybkość wykonania, a zespół zgodzi się na zagwarantowanie, że unsafe jest uzasadnione, to rzadko można go pokonać.

const BYTES_IN_INT32 = 4 

func UnsafeCaseInt32ToBytes(val int32) []byte { 
    hdr := reflect.SliceHeader{Data: uintptr(unsafe.Pointer(&val)), Len: BYTES_IN_INT32, Cap: BYTES_IN_INT32} 
    return *(*[]byte)(unsafe.Pointer(&hdr)) 
} 

func UnsafeCastInt32sToBytes(ints []int32) []byte {   
    length := len(ints) * BYTES_IN_INT32 
    hdr := reflect.SliceHeader{Data: uintptr(unsafe.Pointer(&ints[0])), Len: length, Cap: length} 
    return *(*[]byte)(unsafe.Pointer(&hdr)) 
} 

* Uwaga: Można użyć raczej SizeOf niż stałej. Trochę mi się podoba.

Aktualizacja: oto kilka Wyniki testów:

BenchmarkB2  20000000  88.7 ns/op 
BenchmarkB4  5000000  309 ns/op 
BenchmarkUnsafe 1000000000 2.25 ns/op 
+5

Nie tylko 'unsafe' bezpieczeństwo pamięci przerwa, wprowadza zagadnienia bajt. –

+0

@ larsmans: Podobnie jak bit przesuwania rzeczy; Co więcej, samo kodowanie/binarne ma świadomość endianów (http://golang.org/pkg/encoding/binary/#pkg-variables). – voidlogic

+7

Nie, zmiana biegów jest bezpieczna. 'x >> 24' otrzyma 8 bitów' x' wyższego rzędu, bez względu na endianness. –

Powiązane problemy