将任意的Golang接口转换为字节数组。

huangapple go评论108阅读模式
英文:

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 (
&quot;encoding/gob&quot;
&quot;bytes&quot;
)
// adapted from http://bretmulvey.com/hash/7.html
func ComputeHash(key interface{}) (uint, error) {
var buf bytes.Buffer
enc := gob.NewEncoder(&amp;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 &lt; len(data)-12; {
a += uint(data[i+1] | data[i+2] &lt;&lt; 8 | data[i+3] &lt;&lt; 16 | data[i+4] &lt;&lt; 24)
i += 4
b += uint(data[i+1] | data[i+2] &lt;&lt; 8 | data[i+3] &lt;&lt; 16 | data[i+4] &lt;&lt; 24)
i += 4
c += uint(data[i+1] | data[i+2] &lt;&lt; 8 | data[i+3] &lt;&lt; 16 | data[i+4] &lt;&lt; 24)
a, b, c = mix(a, b, c);
}
c += uint(len(data))
if i &lt; len(data) {
a += uint(data[i])
i++
}
if i &lt; len(data) {
a += uint(data[i] &lt;&lt; 8)
i++
}
if i &lt; len(data) {
a += uint(data[i] &lt;&lt; 16)
i++
}
if i &lt; len(data) {
a += uint(data[i] &lt;&lt; 24)
i++
}
if i &lt; len(data) {
b += uint(data[i])
i++
}
if i &lt; len(data) {
b += uint(data[i] &lt;&lt; 8)
i++
}
if i &lt; len(data) {
b += uint(data[i] &lt;&lt; 16)
i++
}
if i &lt; len(data) {
b += uint(data[i] &lt;&lt; 24)
i++
}
if i &lt; len(data) {
c += uint(data[i] &lt;&lt; 8)
i++
}
if i &lt; len(data) {
c += uint(data[i] &lt;&lt; 16)
i++
}
if i &lt; len(data) {
c += uint(data[i] &lt;&lt; 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&gt;&gt;13);
b -= c; b -= a; b ^= (a&lt;&lt;8);
c -= a; c -= b; c ^= (b&gt;&gt;13);
a -= b; a -= c; a ^= (c&gt;&gt;12);
b -= c; b -= a; b ^= (a&lt;&lt;16);
c -= a; c -= b; c ^= (b&gt;&gt;5);
a -= b; a -= c; a ^= (c&gt;&gt;3);
b -= c; b -= a; b ^= (a&lt;&lt;10);
c -= a; c -= b; c ^= (b&gt;&gt;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 (
&quot;encoding/gob&quot;
&quot;bytes&quot;
)
func GetBytes(key interface{}) ([]byte, error) {
var buf bytes.Buffer
enc := gob.NewEncoder(&amp;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(&quot;%v&quot;, 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.

huangapple
  • 本文由 发表于 2014年4月11日 12:55:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/23003793.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定