そのため、xxhash をcgoの使用から Go のネイティブ 9p C に移植していますが、かなり奇妙な問題が発生しています。
ハッシュ関数は、cgo 関数として呼び出された場合は問題なく動作しますが、「ネイティブ」バージョンを使用しようとすると、間違ったハッシュが返されます。
私はそれを機能させるのに十分な C を知っていますが、問題を報告する前に、私が何か間違ったことをしていないことを確認したいと思います。
xxhash.go
:
//#include "xxhash_9p.c"
//import "C" //uncomment this and comment the next line for the cgo version
func XXH32_test(in unsafe.Pointer, l uint32, seed uint32) uint32
func GoXXH32(in []byte, seed uint32) (h uint32) {
//omitted, full version in the gist above
}
func main() {
b := []byte("ABCDEFGLAALSDLSD:LSDL:DL:DL:SDL:SL:DSL:DL:DSL:DL:{W{EOQWExzghp[[")
fmt.Println(XXH32_test(unsafe.Pointer(&b[0]), uint32(len(b)), 0)) //uncomment this and comment the next line for the cgo version
//fmt.Println(C.XXH32_test(unsafe.Pointer(&b[0]), C.uint(len(b)), 0))
fmt.Println(GoXXH32(b, 0)) //this is tested against the C implementation and it's the right hash.
}
xxhash_9p.c
:
#define PRIME32_1 2654435761U
#define PRIME32_2 2246822519U
#define PRIME32_3 3266489917U
#define PRIME32_4 668265263U
#define PRIME32_5 374761393U
#define U32 unsigned int
typedef struct _U32_S { U32 v; } U32_S;
#define A32(x) (((U32_S *)(x))->v)
U32 ·XXH32_test(const void* input, U32 len, U32 seed) {
//static U32 XXH32_test(const void* input, U32 len, U32 seed) {
const char* p = (const char*)input;
const char* bEnd = p + len;
U32 h32;
#define XXH_get32bits(p) A32(p)
#define XXH_rotl32(x,r) ((x << r) | (x >> (32 - r)))
if (len>=16) {
const char* const limit = bEnd - 16;
U32 v1 = seed + PRIME32_1 + PRIME32_2;
U32 v2 = seed + PRIME32_2;
U32 v3 = seed + 0;
U32 v4 = seed - PRIME32_1;
do
{
v1 += XXH_get32bits(p) * PRIME32_2; v1 = XXH_rotl32(v1, 13); v1 *= PRIME32_1; p+=4;
v2 += XXH_get32bits(p) * PRIME32_2; v2 = XXH_rotl32(v2, 13); v2 *= PRIME32_1; p+=4;
v3 += XXH_get32bits(p) * PRIME32_2; v3 = XXH_rotl32(v3, 13); v3 *= PRIME32_1; p+=4;
v4 += XXH_get32bits(p) * PRIME32_2; v4 = XXH_rotl32(v4, 13); v4 *= PRIME32_1; p+=4;
} while (p<=limit);
h32 = XXH_rotl32(v1, 1) + XXH_rotl32(v2, 7) + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18);
}
else
{
h32 = seed + PRIME32_5;
}
h32 += (unsigned long) len;
while (p<=bEnd-4) {
h32 += XXH_get32bits(p) * PRIME32_3;
h32 = XXH_rotl32(h32, 17) * PRIME32_4 ;
p+=4;
}
while (p<bEnd) {
h32 += (*p) * PRIME32_5;
h32 = XXH_rotl32(h32, 11) * PRIME32_1 ;
p++;
}
h32 ^= h32 >> 15;
h32 *= PRIME32_2;
h32 ^= h32 >> 13;
h32 *= PRIME32_3;
h32 ^= h32 >> 16;
return h32;
}
走る:
$ go build && ./nocgo #9p native
134316512
981225178
$ go build && ./nocgo #cgo
981225178
981225178
TL;DR:
AC 関数は、Go の 6c を介して使用すると間違った値を返し、CGO を介して呼び出すと、同じ正確な C 関数が正しい値を返します。
//編集
この問題について回答がありましたが、修正されることはなく、最終的に 9p ツールチェーンはなくなります。
mi...@golang.orgから:
Cコンパイラは最終的になくなります。そのための計画を立てるので、それに頼らないでください。
Plan 9 C コンパイラは ANSI に完全に準拠していないことに注意してください。バグを修正する予定はありません (コンパイラとその入力の両方を制御しているため、バグを回避するだけです)。