英文:
Converting int32 to byte array in go
问题
我正在使用big.NewInt(int64(e)).Bytes()将int32转换为字节数组。
有没有更优雅的方法来做这个?
我期望AQAB是e的base64编码值
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))
}
输出:
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
编辑:
我对b2和b4进行了基准测试:
b2 1000000000 68.1 ns/op 8 B/op 1 allocs/op
b4 200000000 248 ns/op 90 B/op 3 allocs/op
我现在将使用b2...
英文:
I am using big.NewInt(int64(e)).Bytes() to convert an int32 to a byte array.
Is there a more elegant way to do this?
I expect AQAB to be the base64 encoded value of 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))
}
Output:
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:
I've benchmarked b2 and b4:
b2 1000000000 68.1 ns/op 8 B/op 1 allocs/op
b4 200000000 248 ns/op 90 B/op 3 allocs/op
I'll use b2 for now...
答案1
得分: 5
对于这种任务,我认为你的首选应该始终使用encoding/binary
,如果不够的话,可以使用位运算。然而,在某些情况下,复制数据的开销太大,或者这些“安全”的解决方案太慢:
虽然我不认为它很优雅,但你可以使用Go的unsafe
和reflect
包来快速完成这个任务。只需记住,这不是复制数据,而是给你提供了另一个“视图”。而且,由于它是不安全的,所以你需要非常小心(你好单元测试和代码审查),并且要记住你正在破坏Go的内存安全性。然而,当执行速度是主要关注点,并且你的团队同意使用unsafe
时,unsafe
很少会被超越。
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))
}
*注意:你可能想使用SizeOf
而不是一个常量。我更喜欢使用常量。
更新:这里是一些基准测试结果:
BenchmarkB2 20000000 88.7 ns/op
BenchmarkB4 5000000 309 ns/op
BenchmarkUnsafe 1000000000 2.25 ns/op
英文:
For this kind of task I think your first options should always be using encoding/binary
and, if that is insufficient, bitwise math. However, in some cases the overhead of copying data is too large or these safe solutions are too slow:
While I would not call it elegant you can use Go's unsafe
and reflect
* packages to do this very quickly. Just remember, this does not copy the data; rather, it just gives you another "view" of it. And being well- unsafe means that you need to be very careful- (hello units tests and code review) and keep in mind you are breaking Go's memory safety. However, when execution speed is the dominating concern and your team agrees unsafe
is warranted, unsafe
can seldom be beat.
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))
}
*Note: You may want to use SizeOf
rather than a constant. I like the constant better.
Update: here are some benchmark results:
BenchmarkB2 20000000 88.7 ns/op
BenchmarkB4 5000000 309 ns/op
BenchmarkUnsafe 1000000000 2.25 ns/op
答案2
得分: -1
这是最直接(也是最短(也是最安全)(也可能是最高效))的方法:
buf.Bytes()
是字节切片类型。
var val uint32 = 42
buf := new(bytes.Buffer)
err := binary.Write(buf, binary.LittleEndian, val)
if err != nil {
fmt.Println("binary.Write 失败:", err)
}
fmt.Printf("% x\n", buf.Bytes())
英文:
This is the most straight forward (and shortest (and safest) (and maybe most performant)) way:
buf.Bytes()
is of type bytes slice.
var val uint32 = 42
buf := new(bytes.Buffer)
err := binary.Write(buf, binary.LittleEndian, val)
if err != nil {
fmt.Println("binary.Write failed:", err)
}
fmt.Printf("% x\n", buf.Bytes())
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论