bytes.go (2003B)
1 package byteutil 2 3 import ( 4 "reflect" 5 "unsafe" 6 ) 7 8 // Copy returns a copy of []byte. 9 func Copy(b []byte) []byte { 10 if b == nil { 11 return nil 12 } 13 p := make([]byte, len(b)) 14 copy(p, b) 15 return p 16 } 17 18 // B2S returns a string representation of []byte without allocation. 19 // 20 // According to the Go spec strings are immutable and byte slices are not. The way this gets implemented is strings under the hood are: 21 // 22 // type StringHeader struct { 23 // Data uintptr 24 // Len int 25 // } 26 // 27 // while slices are: 28 // 29 // type SliceHeader struct { 30 // Data uintptr 31 // Len int 32 // Cap int 33 // } 34 // 35 // because being mutable, you can change the data, length etc, but the string has to promise to be read-only to all who get copies of it. 36 // 37 // So in practice when you do a conversion of `string(byteSlice)` it actually performs an allocation because it has to copy the contents of the byte slice into a safe read-only state. 38 // 39 // Being that the shared fields are in the same struct indices (no different offsets), means that if you have a byte slice you can "forcibly" cast it to a string. Which in a lot of situations can be risky, because then it means you have a string that is NOT immutable, as if someone changes the data in the originating byte slice then the string will reflect that change! Now while this does seem hacky, and it _kind_ of is, it is something that you see performed in the standard library. If you look at the definition for `strings.Builder{}.String()` you'll see this :) 40 func B2S(b []byte) string { 41 return *(*string)(unsafe.Pointer(&b)) 42 } 43 44 // S2B returns a []byte representation of string without allocation (minus slice header). 45 // See B2S() code comment, and this function's implementation for a better understanding. 46 func S2B(s string) []byte { 47 var b []byte 48 49 // Get byte + string headers 50 bh := (*reflect.SliceHeader)(unsafe.Pointer(&b)) 51 sh := (*reflect.StringHeader)(unsafe.Pointer(&s)) 52 53 // Manually set bytes to string 54 bh.Data = sh.Data 55 bh.Len = sh.Len 56 bh.Cap = sh.Len 57 58 return b 59 }