英文:
Convert arbitrary Golang interface to byte array
问题
我正在尝试编写一个可以接受所有数据类型的哈希函数。一旦进入函数,我将数据处理为字节数组。我在强制将任意的interface{}
类型转换为字节数组时遇到了困难。
我尝试使用binary
包,但它似乎依赖于传入的数据类型。Write()
函数的一个参数(文档)需要知道参数的字节顺序。
所有数据类型的大小都是字节的倍数(甚至是布尔类型),所以理论上应该很简单。
以下是相关的代码:
package bloom
import (
"encoding/gob"
"bytes"
)
// 从 http://bretmulvey.com/hash/7.html 改编
func ComputeHash(key interface{}) (uint, error) {
var buf bytes.Buffer
enc := gob.NewEncoder(&buf)
err := enc.Encode(key)
if err != nil {
return 0, err
}
data := buf.Bytes()
var a, b, c uint
a, b = 0x9e3779b9, 0x9e3779b9
c = 0
i := 0
for i = 0; i < len(data)-12; {
a += uint(data[i+1] | data[i+2]<<8 | data[i+3]<<16 | data[i+4]<<24)
i += 4
b += uint(data[i+1] | data[i+2]<<8 | data[i+3]<<16 | data[i+4]<<24)
i += 4
c += uint(data[i+1] | data[i+2]<<8 | data[i+3]<<16 | data[i+4]<<24)
a, b, c = mix(a, b, c)
}
c += uint(len(data))
if i < len(data) {
a += uint(data[i])
i++
}
if i < len(data) {
a += uint(data[i]<<8)
i++
}
if i < len(data) {
a += uint(data[i]<<16)
i++
}
if i < len(data) {
a += uint(data[i]<<24)
i++
}
if i < len(data) {
b += uint(data[i])
i++
}
if i < len(data) {
b += uint(data[i]<<8)
i++
}
if i < len(data) {
b += uint(data[i]<<16)
i++
}
if i < len(data) {
b += uint(data[i]<<24)
i++
}
if i < len(data) {
c += uint(data[i]<<8)
i++
}
if i < len(data) {
c += uint(data[i]<<16)
i++
}
if i < len(data) {
c += uint(data[i]<<24)
i++
}
a, b, c = mix(a, b, c)
return c, nil
}
func mix(a, b, c uint) (uint, uint, uint){
a -= b; a -= c; a ^= (c>>13)
b -= c; b -= a; b ^= (a<<8)
c -= a; c -= b; c ^= (b>>13)
a -= b; a -= c; a ^= (c>>12)
b -= c; b -= a; b ^= (a<<16)
c -= a; c -= b; c ^= (b>>5)
a -= b; a -= c; a ^= (c>>3)
b -= c; b -= a; b ^= (a<<10)
c -= a; c -= b; c ^= (b>>15)
return a, b, c
}
希望对你有帮助!
英文:
I'm trying to write a hash that will accept all datatypes. Once in the function, I handle the data as a byte array. I'm having trouble figuring out how to cast an arbitrary interface{}
to a byte array.
I tried using the binary package but it seemed to depend on the type of data passed in. One of the parameters of the Write()
fn (docs) required knowing the byte order of the parameter.
All datatype sizes are some multiple of a byte (even the bool), so this should be simple in theory.
Code in question below,
package bloom
import (
"encoding/gob"
"bytes"
)
// adapted from http://bretmulvey.com/hash/7.html
func ComputeHash(key interface{}) (uint, error) {
var buf bytes.Buffer
enc := gob.NewEncoder(&buf)
err := enc.Encode(key)
if err != nil {
return 0, err
}
data := buf.Bytes()
var a, b, c uint
a, b = 0x9e3779b9, 0x9e3779b9
c = 0;
i := 0;
for i = 0; i < len(data)-12; {
a += uint(data[i+1] | data[i+2] << 8 | data[i+3] << 16 | data[i+4] << 24)
i += 4
b += uint(data[i+1] | data[i+2] << 8 | data[i+3] << 16 | data[i+4] << 24)
i += 4
c += uint(data[i+1] | data[i+2] << 8 | data[i+3] << 16 | data[i+4] << 24)
a, b, c = mix(a, b, c);
}
c += uint(len(data))
if i < len(data) {
a += uint(data[i])
i++
}
if i < len(data) {
a += uint(data[i] << 8)
i++
}
if i < len(data) {
a += uint(data[i] << 16)
i++
}
if i < len(data) {
a += uint(data[i] << 24)
i++
}
if i < len(data) {
b += uint(data[i])
i++
}
if i < len(data) {
b += uint(data[i] << 8)
i++
}
if i < len(data) {
b += uint(data[i] << 16)
i++
}
if i < len(data) {
b += uint(data[i] << 24)
i++
}
if i < len(data) {
c += uint(data[i] << 8)
i++
}
if i < len(data) {
c += uint(data[i] << 16)
i++
}
if i < len(data) {
c += uint(data[i] << 24)
i++
}
a, b, c = mix(a, b, c)
return c, nil
}
func mix(a, b, c uint) (uint, uint, uint){
a -= b; a -= c; a ^= (c>>13);
b -= c; b -= a; b ^= (a<<8);
c -= a; c -= b; c ^= (b>>13);
a -= b; a -= c; a ^= (c>>12);
b -= c; b -= a; b ^= (a<<16);
c -= a; c -= b; c ^= (b>>5);
a -= b; a -= c; a ^= (c>>3);
b -= c; b -= a; b ^= (a<<10);
c -= a; c -= b; c ^= (b>>15);
return a, b, c
}
答案1
得分: 77
其他代码问题让我远离了之前的gob
包,结果证明这是正确的方式,就像@nvcnvn建议的那样。下面是解决这个问题的相关代码:
package bloom
import (
"encoding/gob"
"bytes"
)
func GetBytes(key interface{}) ([]byte, error) {
var buf bytes.Buffer
enc := gob.NewEncoder(&buf)
err := enc.Encode(key)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}
英文:
Other problems in my code led me away from the gob
package earlier, turns out it was the proper way as @nvcnvn suggested. Relevant code on how to solve this issue below:
package bloom
import (
"encoding/gob"
"bytes"
)
func GetBytes(key interface{}) ([]byte, error) {
var buf bytes.Buffer
enc := gob.NewEncoder(&buf)
err := enc.Encode(key)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}
答案2
得分: 5
另一种将interface{}
转换为[]bytes
的方法是使用fmt包。
/*
* 将变量`key`从interface{}转换为[]byte
*/
byteKey := []byte(fmt.Sprintf("%v", key.(interface{})))
fmt.Sprintf将interface值转换为字符串。
[]byte将字符串值转换为字节。
※ 注意 ※ 如果interface{}值是指针,则此方法不起作用。请查看@PassKit的评论。
英文:
Another way to convert interface{}
to []bytes
is to use a fmt package.
/*
* Convert variable `key` from interface{} to []byte
*/
byteKey := []byte(fmt.Sprintf("%v", key.(interface{})))
fmt.Sprintf converts interface value to string.<br>
[]byte converts string value to byte.
※ Note ※ This method does not work if interface{} value is a pointer. Please find @PassKit's comment below.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论