tz: use build tags for different implemenations

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
This commit is contained in:
Evgenii Stratonikov 2022-03-09 16:58:21 +03:00 committed by Alex Vanin
parent 3491e7c5ea
commit 0e0d28e82f
15 changed files with 236 additions and 546 deletions

View file

@ -3,9 +3,10 @@
tmpfile=$(mktemp /tmp/random-file.XXXXXX) tmpfile=$(mktemp /tmp/random-file.XXXXXX)
dd if=/dev/urandom of=$tmpfile bs=$1 count=1 dd if=/dev/urandom of=$tmpfile bs=$1 count=1
go build ./cmd/tzsum && \ go build ./cmd/tzsum || exit 1
for impl in avx avx2 avx2inline purego; do
for impl in avx avx2 generic; do
echo $impl implementation: echo $impl implementation:
/usr/bin/env time ./tzsum -name $tmpfile -impl $impl time ./tzsum -name $tmpfile -impl $impl
echo echo
done done

View file

@ -11,6 +11,7 @@ import (
"runtime/pprof" "runtime/pprof"
"github.com/nspcc-dev/tzhash/tz" "github.com/nspcc-dev/tzhash/tz"
"golang.org/x/sys/cpu"
) )
var ( var (
@ -46,18 +47,23 @@ func main() {
f = os.Stdin f = os.Stdin
} }
// Override CPU feature flags to make sure a proper backend is used.
var h hash.Hash var h hash.Hash
switch *hashimpl { switch *hashimpl {
case "avx": case "avx":
h = tz.NewWith(tz.AVX) cpu.X86.HasAVX = true
case "avx2": cpu.X86.HasAVX2 = false
h = tz.NewWith(tz.AVX2)
case "avx2inline":
h = tz.NewWith(tz.AVX2Inline)
case "purego":
h = tz.NewWith(tz.PureGo)
default:
h = tz.New() h = tz.New()
case "avx2":
cpu.X86.HasAVX = true
cpu.X86.HasAVX2 = true
h = tz.New()
case "generic":
cpu.X86.HasAVX = false
cpu.X86.HasAVX2 = false
h = tz.New()
default:
log.Fatalf("Invalid backend: %s", *hashimpl)
} }
if _, err := io.Copy(h, f); err != nil { if _, err := io.Copy(h, f); err != nil {

View file

@ -1,75 +0,0 @@
package tz
import (
"hash"
"math"
)
type digest struct {
x [4]GF127
}
// type assertion
var _ hash.Hash = (*digest)(nil)
var (
minmax = [2]GF127{{0, 0}, {math.MaxUint64, math.MaxUint64}}
x127x63 = GF127{1 << 63, 1 << 63} //nolint:deadcode,varcheck
)
func newAVX() *digest {
d := new(digest)
d.Reset()
return d
}
func (d *digest) Sum(in []byte) []byte {
// Make a copy of d so that caller can keep writing and summing.
d0 := *d
h := d0.checkSum()
return append(in, h[:]...)
}
func (d *digest) checkSum() [Size]byte {
return d.byteArray()
}
func (d *digest) byteArray() (b [Size]byte) {
copy(b[:], d.x[0].ByteArray())
copy(b[16:], d.x[1].ByteArray())
copy(b[32:], d.x[2].ByteArray())
copy(b[48:], d.x[3].ByteArray())
return
}
func (d *digest) Reset() {
d.x[0] = GF127{1, 0}
d.x[1] = GF127{0, 0}
d.x[2] = GF127{0, 0}
d.x[3] = GF127{1, 0}
}
func (d *digest) Write(data []byte) (n int, err error) {
n = len(data)
for _, b := range data {
mulBitRight(&d.x[0], &d.x[1], &d.x[2], &d.x[3], &minmax[(b>>7)&1])
mulBitRight(&d.x[0], &d.x[1], &d.x[2], &d.x[3], &minmax[(b>>6)&1])
mulBitRight(&d.x[0], &d.x[1], &d.x[2], &d.x[3], &minmax[(b>>5)&1])
mulBitRight(&d.x[0], &d.x[1], &d.x[2], &d.x[3], &minmax[(b>>4)&1])
mulBitRight(&d.x[0], &d.x[1], &d.x[2], &d.x[3], &minmax[(b>>3)&1])
mulBitRight(&d.x[0], &d.x[1], &d.x[2], &d.x[3], &minmax[(b>>2)&1])
mulBitRight(&d.x[0], &d.x[1], &d.x[2], &d.x[3], &minmax[(b>>1)&1])
mulBitRight(&d.x[0], &d.x[1], &d.x[2], &d.x[3], &minmax[(b>>0)&1])
}
return
}
func (d *digest) Size() int {
return Size
}
func (d *digest) BlockSize() int {
return hashBlockSize
}
func mulBitRight(c00, c01, c10, c11, e *GF127)

View file

@ -1,62 +0,0 @@
package tz
import (
"hash"
"github.com/nspcc-dev/tzhash/gf127"
)
type digest2 struct {
x [2]gf127.GF127x2
}
// type assertion
var _ hash.Hash = (*digest2)(nil)
func newAVX2() *digest2 {
d := new(digest2)
d.Reset()
return d
}
func (d *digest2) Write(data []byte) (n int, err error) {
n = len(data)
for _, b := range data {
mulBitRightx2(&d.x[0], &d.x[1], &minmax[(b>>7)&1])
mulBitRightx2(&d.x[0], &d.x[1], &minmax[(b>>6)&1])
mulBitRightx2(&d.x[0], &d.x[1], &minmax[(b>>5)&1])
mulBitRightx2(&d.x[0], &d.x[1], &minmax[(b>>4)&1])
mulBitRightx2(&d.x[0], &d.x[1], &minmax[(b>>3)&1])
mulBitRightx2(&d.x[0], &d.x[1], &minmax[(b>>2)&1])
mulBitRightx2(&d.x[0], &d.x[1], &minmax[(b>>1)&1])
mulBitRightx2(&d.x[0], &d.x[1], &minmax[(b>>0)&1])
}
return
}
func (d *digest2) Sum(in []byte) []byte {
// Make a copy of d so that caller can keep writing and summing.
d0 := *d
h := d0.checkSum()
return append(in, h[:]...)
}
func (d *digest2) Reset() {
d.x[0] = gf127.GF127x2{GF127{1, 0}, GF127{0, 0}}
d.x[1] = gf127.GF127x2{GF127{0, 0}, GF127{1, 0}}
}
func (d *digest2) Size() int { return Size }
func (d *digest2) BlockSize() int { return hashBlockSize }
func (d *digest2) checkSum() (b [Size]byte) {
// Matrix is stored transposed,
// but we need to use order consistent with digest.
h := d.x[0].ByteArray()
copy(b[:], h[:16])
copy(b[32:], h[16:])
h = d.x[1].ByteArray()
copy(b[16:], h[:16])
copy(b[48:], h[16:])
return
}
func mulBitRightx2(c00c10 *gf127.GF127x2, c01c11 *gf127.GF127x2, e *GF127)

View file

@ -1,55 +0,0 @@
package tz
import (
"hash"
"github.com/nspcc-dev/tzhash/gf127"
)
type digest3 struct {
x [2]gf127.GF127x2
}
// type assertion
var _ hash.Hash = (*digest3)(nil)
func newAVX2Inline() *digest3 {
d := new(digest3)
d.Reset()
return d
}
func (d *digest3) Write(data []byte) (n int, err error) {
n = len(data)
if len(data) != 0 {
mulByteSliceRightx2(&d.x[0], &d.x[1], n, &data[0])
}
return
}
func (d *digest3) Sum(in []byte) []byte {
// Make a copy of d so that caller can keep writing and summing.
d0 := *d
h := d0.checkSum()
return append(in, h[:]...)
}
func (d *digest3) Reset() {
d.x[0] = gf127.GF127x2{GF127{1, 0}, GF127{0, 0}}
d.x[1] = gf127.GF127x2{GF127{0, 0}, GF127{1, 0}}
}
func (d *digest3) Size() int { return Size }
func (d *digest3) BlockSize() int { return hashBlockSize }
func (d *digest3) checkSum() (b [Size]byte) {
// Matrix is stored transposed,
// but we need to use order consistent with digest.
h := d.x[0].ByteArray()
copy(b[:], h[:16])
copy(b[32:], h[16:])
h = d.x[1].ByteArray()
copy(b[16:], h[:16])
copy(b[48:], h[16:])
return
}
func mulByteSliceRightx2(c00c10 *gf127.GF127x2, c01c11 *gf127.GF127x2, n int, data *byte)

View file

@ -1,62 +0,0 @@
package tz
import (
"hash"
)
type digest4 struct {
x [4]GF127
}
// type assertion
var _ hash.Hash = (*digest4)(nil)
func newAVXInline() *digest4 {
d := new(digest4)
d.Reset()
return d
}
func (d *digest4) Sum(in []byte) []byte {
// Make a copy of d so that caller can keep writing and summing.
d0 := *d
h := d0.checkSum()
return append(in, h[:]...)
}
func (d *digest4) checkSum() [Size]byte {
return d.byteArray()
}
func (d *digest4) byteArray() (b [Size]byte) {
copy(b[:], d.x[0].ByteArray())
copy(b[16:], d.x[1].ByteArray())
copy(b[32:], d.x[2].ByteArray())
copy(b[48:], d.x[3].ByteArray())
return
}
func (d *digest4) Reset() {
d.x[0] = GF127{1, 0}
d.x[1] = GF127{0, 0}
d.x[2] = GF127{0, 0}
d.x[3] = GF127{1, 0}
}
func (d *digest4) Write(data []byte) (n int, err error) {
n = len(data)
for _, b := range data {
mulByteRight(&d.x[0], &d.x[1], &d.x[2], &d.x[3], b)
}
return
}
func (d *digest4) Size() int {
return Size
}
func (d *digest4) BlockSize() int {
return hashBlockSize
}
func mulByteRight(c00, c01, c10, c11 *GF127, b byte)

122
tz/digest.go Normal file
View file

@ -0,0 +1,122 @@
package tz
import (
"github.com/nspcc-dev/tzhash/gf127"
)
const (
// Size is the size of a Tillich-Zémor hash sum in bytes.
Size = 64
hashBlockSize = 128
)
type digest struct {
// Stores matrix cells in the following order:
// [ 0 2 ]
// [ 1 3 ]
// This is done to reuse the same digest between generic
// and AVX2 implementation.
x [4]GF127
}
// New returns a new hash.Hash computing the Tillich-Zémor checksum.
func New() *digest {
d := new(digest)
d.Reset()
return d
}
// Sum returns Tillich-Zémor checksum of data.
func Sum(data []byte) [Size]byte {
d := new(digest)
_, _ = d.Write(data) // no errors
return d.checkSum()
}
func (d *digest) Sum(in []byte) []byte {
// Make a copy of d so that caller can keep writing and summing.
d0 := *d
h := d0.checkSum()
return append(in, h[:]...)
}
func (d *digest) checkSum() [Size]byte {
return d.byteArray()
}
func (d *digest) byteArray() (b [Size]byte) {
t := d.x[0].ByteArray()
copy(b[:], t[:])
t = d.x[2].ByteArray()
copy(b[16:], t[:])
t = d.x[1].ByteArray()
copy(b[32:], t[:])
t = d.x[3].ByteArray()
copy(b[48:], t[:])
return
}
func (d *digest) Reset() {
d.x[0] = GF127{1, 0}
d.x[1] = GF127{0, 0}
d.x[2] = GF127{0, 0}
d.x[3] = GF127{1, 0}
}
func (d *digest) Write(data []byte) (n int, err error) {
return write(d, data)
}
func writeGeneric(d *digest, data []byte) (n int, err error) {
n = len(data)
tmp := new(GF127)
for _, b := range data {
mulBitRightGeneric(&d.x[0], &d.x[1], &d.x[2], &d.x[3], b&0x80 != 0, tmp)
mulBitRightGeneric(&d.x[0], &d.x[1], &d.x[2], &d.x[3], b&0x40 != 0, tmp)
mulBitRightGeneric(&d.x[0], &d.x[1], &d.x[2], &d.x[3], b&0x20 != 0, tmp)
mulBitRightGeneric(&d.x[0], &d.x[1], &d.x[2], &d.x[3], b&0x10 != 0, tmp)
mulBitRightGeneric(&d.x[0], &d.x[1], &d.x[2], &d.x[3], b&0x08 != 0, tmp)
mulBitRightGeneric(&d.x[0], &d.x[1], &d.x[2], &d.x[3], b&0x04 != 0, tmp)
mulBitRightGeneric(&d.x[0], &d.x[1], &d.x[2], &d.x[3], b&0x02 != 0, tmp)
mulBitRightGeneric(&d.x[0], &d.x[1], &d.x[2], &d.x[3], b&0x01 != 0, tmp)
}
return
}
func (d *digest) Size() int {
return Size
}
func (d *digest) BlockSize() int {
return hashBlockSize
}
func mulBitRightGeneric(c00, c10, c01, c11 *GF127, bit bool, tmp *GF127) {
if bit {
*tmp = *c00
gf127.Mul10(c00, c00)
gf127.Add(c00, c01, c00)
gf127.Mul11(tmp, tmp)
gf127.Add(c01, tmp, c01)
*tmp = *c10
gf127.Mul10(c10, c10)
gf127.Add(c10, c11, c10)
gf127.Mul11(tmp, tmp)
gf127.Add(c11, tmp, c11)
} else {
*tmp = *c00
gf127.Mul10(c00, c00)
gf127.Add(c00, c01, c00)
*c01 = *tmp
*tmp = *c10
gf127.Mul10(c10, c10)
gf127.Add(c10, c11, c10)
*c11 = *tmp
}
}

View file

@ -59,29 +59,3 @@ finish:
VMOVDQU Y0, (AX) VMOVDQU Y0, (AX)
RET RET
// func mulBitRightx2(c00c10, c01c11 *[4]uint64, e *[2]uint64)
TEXT ·mulBitRightx2(SB), NOSPLIT, $0
MOVQ c00c10+0(FP), AX
VMOVDQU (AX), Y0
MOVQ c01c11+8(FP), BX
VMOVDQU (BX), Y8
VPSLLQ $1, Y0, Y1
VPALIGNR $8, Y1, Y0, Y2
VPSRLQ $63, Y2, Y2
VPXOR Y1, Y2, Y2
VPSRLQ $63, Y1, Y3
VPSLLQ $63, Y3, Y3
VPUNPCKHQDQ Y3, Y3, Y3
VPXOR Y2, Y3, Y3
MOVQ e+16(FP), CX
VBROADCASTI128 (CX), Y2
VPXOR Y3, Y8, Y3
VPAND Y3, Y2, Y4
VPXOR Y4, Y0, Y8
VMOVDQU Y8, (BX)
VMOVDQU Y3, (AX)
RET

View file

@ -29,50 +29,13 @@
VANDPD X2, X5, X3 \ VANDPD X2, X5, X3 \
VXORPD X9, X3, X3 VXORPD X9, X3, X3
// func mulBitRight(c00, c01, c10, c11, e *[2]uint64)
TEXT ·mulBitRight(SB), NOSPLIT, $0
MOVQ c00+0(FP), AX
VMOVDQU (AX), X0
VMOVDQU X0, X8 // remember c00 value
MOVQ c01+8(FP), BX
VMOVDQU (BX), X1
MOVQ c10+16(FP), CX
VMOVDQU (CX), X2
VMOVDQU X2, X9 // remember c10 value
MOVQ c11+24(FP), DX
VMOVDQU (DX), X3
VPXOR X13, X13, X13 // Y13 = 0x0000...
VPCMPEQB X14, X14, X14 // Y14 = 0xFFFF...
VPSUBQ X14, X13, X13
VPSLLQ $63, X13, X14
mul2(X0, X5, X6, X7) // c00 *= 2
VXORPD X5, X1, X0 // c00 += c01
mul2(X2, X5, X6, X7) // c10 *= 2
VXORPD X3, X5, X2 // c10 += c11
MOVQ e+32(FP), CX
VMOVDQU (CX), X5
VANDPD X0, X5, X1 // c01 = c00 + e
VXORPD X8, X1, X1 // c01 += X8 (old c00)
VANDPD X2, X5, X3 // c11 = c10 + e
VXORPD X9, X3, X3 // c11 += x9 (old c10)
VMOVDQU X0, (AX)
MOVQ c10+16(FP), CX
VMOVDQU X2, (CX)
VMOVDQU X1, (BX)
VMOVDQU X3, (DX)
RET
TEXT ·mulByteRight(SB), NOSPLIT, $0 TEXT ·mulByteRight(SB), NOSPLIT, $0
MOVQ c00+0(FP), AX MOVQ c00+0(FP), AX
VMOVDQU (AX), X0 VMOVDQU (AX), X0
MOVQ c01+8(FP), BX MOVQ c10+8(FP), CX
VMOVDQU (BX), X1
MOVQ c10+16(FP), CX
VMOVDQU (CX), X2 VMOVDQU (CX), X2
MOVQ c01+16(FP), BX
VMOVDQU (BX), X1
MOVQ c11+24(FP), DX MOVQ c11+24(FP), DX
VMOVDQU (DX), X3 VMOVDQU (DX), X3
MOVQ $0, CX MOVQ $0, CX
@ -98,7 +61,7 @@ TEXT ·mulByteRight(SB), NOSPLIT, $0
mulBit($0) mulBit($0)
VMOVDQU X0, (AX) VMOVDQU X0, (AX)
MOVQ c10+16(FP), CX MOVQ c10+8(FP), CX
VMOVDQU X2, (CX) VMOVDQU X2, (CX)
VMOVDQU X1, (BX) VMOVDQU X1, (BX)
MOVQ c11+24(FP), DX MOVQ c11+24(FP), DX

8
tz/digest_generic.go Normal file
View file

@ -0,0 +1,8 @@
//go:build !(amd64 && !generic)
// +build !amd64 generic
package tz
func write(d *digest, data []byte) (int, error) {
return writeGeneric(d, data)
}

39
tz/digets_amd64.go Normal file
View file

@ -0,0 +1,39 @@
//go:build amd64 && !generic
// +build amd64,!generic
package tz
import (
"github.com/nspcc-dev/tzhash/gf127"
"golang.org/x/sys/cpu"
)
func write(d *digest, data []byte) (n int, err error) {
switch {
case cpu.X86.HasAVX && cpu.X86.HasAVX2:
return writeAVX2(d, data)
case cpu.X86.HasAVX:
return writeAVX(d, data)
default:
return writeGeneric(d, data)
}
}
func writeAVX2(d *digest, data []byte) (n int, err error) {
n = len(data)
if len(data) != 0 {
mulByteSliceRightx2(&d.x[0], &d.x[2], n, &data[0])
}
return
}
func writeAVX(d *digest, data []byte) (n int, err error) {
n = len(data)
for _, b := range data {
mulByteRight(&d.x[0], &d.x[1], &d.x[2], &d.x[3], b)
}
return
}
func mulByteRight(c00, c01, c10, c11 *GF127, b byte)
func mulByteSliceRightx2(c00c10 *gf127.GF127, c01c11 *gf127.GF127, n int, data *byte)

View file

@ -6,95 +6,8 @@ package tz
import ( import (
"errors" "errors"
"hash"
"golang.org/x/sys/cpu"
) )
type Implementation int
const (
// Size is the size of a Tillich-Zemor hash sum in bytes.
Size = 64
hashBlockSize = 128
_ Implementation = iota
AVX
AVX2
AVX2Inline
PureGo
AVXInline
)
var (
hasAVX = cpu.X86.HasAVX
// Having AVX2 does not guarantee
// that AVX is also present.
hasAVX2 = cpu.X86.HasAVX2 && hasAVX
)
func (impl Implementation) String() string {
switch impl {
case AVX:
return "AVX"
case AVXInline:
return "AVXInline"
case AVX2:
return "AVX2"
case AVX2Inline:
return "AVX2Inline"
case PureGo:
return "PureGo"
default:
return "UNKNOWN"
}
}
func NewWith(impl Implementation) hash.Hash {
switch impl {
case AVX:
return newAVX()
case AVXInline:
return newAVXInline()
case AVX2:
return newAVX2()
case AVX2Inline:
return newAVX2Inline()
case PureGo:
return newPure()
default:
return New()
}
}
// New returns a new hash.Hash computing the Tillich-Zémor checksum.
func New() hash.Hash {
if hasAVX2 {
return newAVX2Inline()
} else if hasAVX {
return newAVXInline()
} else {
return newPure()
}
}
// Sum returns Tillich-Zémor checksum of data.
func Sum(data []byte) [Size]byte {
if hasAVX2 {
d := newAVX2Inline()
_, _ = d.Write(data) // no errors
return d.checkSum()
} else if hasAVX {
d := newAVXInline()
_, _ = d.Write(data) // no errors
return d.checkSum()
} else {
d := newPure()
_, _ = d.Write(data) // no errors
return d.checkSum()
}
}
// Concat performs combining of hashes based on homomorphic property. // Concat performs combining of hashes based on homomorphic property.
func Concat(hs [][]byte) ([]byte, error) { func Concat(hs [][]byte) ([]byte, error) {
var b, c sl2 var b, c sl2

View file

@ -2,38 +2,29 @@ package tz
import ( import (
"encoding/hex" "encoding/hex"
"fmt"
"io" "io"
"math/rand" "math/rand"
"testing" "testing"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"golang.org/x/sys/cpu"
) )
const benchDataSize = 100000 const benchDataSize = 100000
var providers = []Implementation{ type arch struct {
AVX, HasAVX bool
AVXInline, HasAVX2 bool
AVX2,
AVX2Inline,
PureGo,
} }
func TestNewWith(t *testing.T) { var backends = []struct {
d := NewWith(AVX) Name string
require.IsType(t, (*digest)(nil), d) arch
}{
d = NewWith(AVXInline) {"AVX", arch{true, false}},
require.IsType(t, (*digest4)(nil), d) {"AVX2", arch{true, true}},
{"Generic", arch{false, false}},
d = NewWith(AVX2)
require.IsType(t, (*digest2)(nil), d)
d = NewWith(AVX2Inline)
require.IsType(t, (*digest3)(nil), d)
d = NewWith(PureGo)
require.IsType(t, (*digestp)(nil), d)
} }
var testCases = []struct { var testCases = []struct {
@ -83,10 +74,12 @@ var testCases = []struct {
} }
func TestHash(t *testing.T) { func TestHash(t *testing.T) {
for i := range providers { for i, b := range backends {
p := providers[i] t.Run(b.Name+" digest", func(t *testing.T) {
t.Run(p.String()+" digest", func(t *testing.T) { prepareArch(t, backends[i].arch)
d := NewWith(p)
fmt.Println("FEATURES:", cpu.X86.HasAVX, cpu.X86.HasAVX2)
d := New()
for _, tc := range testCases { for _, tc := range testCases {
d.Reset() d.Reset()
_, _ = d.Write(tc.input) _, _ = d.Write(tc.input)
@ -97,6 +90,20 @@ func TestHash(t *testing.T) {
} }
} }
func prepareArch(t testing.TB, b arch) {
realCPU := cpu.X86
if !realCPU.HasAVX2 && b.HasAVX2 || !realCPU.HasAVX && b.HasAVX {
t.Skip("Underlying CPU doesn't support necessary features")
} else {
t.Cleanup(func() {
cpu.X86.HasAVX = realCPU.HasAVX
cpu.X86.HasAVX2 = realCPU.HasAVX2
})
cpu.X86.HasAVX = b.HasAVX
cpu.X86.HasAVX2 = b.HasAVX2
}
}
func newBuffer() (data []byte) { func newBuffer() (data []byte) {
data = make([]byte, benchDataSize) data = make([]byte, benchDataSize)
@ -110,20 +117,20 @@ func newBuffer() (data []byte) {
func BenchmarkSum(b *testing.B) { func BenchmarkSum(b *testing.B) {
data := newBuffer() data := newBuffer()
size := int64(len(data))
for i := range providers { for i := range backends {
p := providers[i] b.Run(backends[i].Name+" digest", func(b *testing.B) {
b.Run(p.String()+" digest", func(b *testing.B) { prepareArch(b, backends[i].arch)
b.ResetTimer() b.ResetTimer()
b.ReportAllocs() b.ReportAllocs()
d := NewWith(p) d := New()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
d.Reset() d.Reset()
_, _ = d.Write(data) _, _ = d.Write(data)
d.Sum(nil) d.Sum(nil)
} }
b.SetBytes(size) b.SetBytes(int64(len(data)))
}) })
} }
} }

View file

@ -1,92 +0,0 @@
package tz
import (
"github.com/nspcc-dev/tzhash/gf127"
)
type digestp struct {
x [4]GF127
}
// New returns a new hash.Hash computing the Tillich-Zémor checksum.
func newPure() *digestp {
d := new(digestp)
d.Reset()
return d
}
func (d *digestp) Sum(in []byte) []byte {
// Make a copy of d so that caller can keep writing and summing.
d0 := *d
h := d0.checkSum()
return append(in, h[:]...)
}
func (d *digestp) checkSum() [Size]byte {
return d.byteArray()
}
func (d *digestp) byteArray() (b [Size]byte) {
for i := 0; i < 4; i++ {
t := d.x[i].ByteArray()
copy(b[i*16:], t[:])
}
return
}
func (d *digestp) Reset() {
d.x[0] = GF127{1, 0}
d.x[1] = GF127{0, 0}
d.x[2] = GF127{0, 0}
d.x[3] = GF127{1, 0}
}
func (d *digestp) Write(data []byte) (n int, err error) {
n = len(data)
tmp := new(GF127)
for _, b := range data {
mulBitRightPure(&d.x[0], &d.x[1], &d.x[2], &d.x[3], b&0x80 != 0, tmp)
mulBitRightPure(&d.x[0], &d.x[1], &d.x[2], &d.x[3], b&0x40 != 0, tmp)
mulBitRightPure(&d.x[0], &d.x[1], &d.x[2], &d.x[3], b&0x20 != 0, tmp)
mulBitRightPure(&d.x[0], &d.x[1], &d.x[2], &d.x[3], b&0x10 != 0, tmp)
mulBitRightPure(&d.x[0], &d.x[1], &d.x[2], &d.x[3], b&0x08 != 0, tmp)
mulBitRightPure(&d.x[0], &d.x[1], &d.x[2], &d.x[3], b&0x04 != 0, tmp)
mulBitRightPure(&d.x[0], &d.x[1], &d.x[2], &d.x[3], b&0x02 != 0, tmp)
mulBitRightPure(&d.x[0], &d.x[1], &d.x[2], &d.x[3], b&0x01 != 0, tmp)
}
return
}
func (d *digestp) Size() int {
return Size
}
func (d *digestp) BlockSize() int {
return hashBlockSize
}
func mulBitRightPure(c00, c01, c10, c11 *GF127, bit bool, tmp *GF127) {
if bit {
*tmp = *c00
gf127.Mul10(c00, c00)
gf127.Add(c00, c01, c00)
gf127.Mul11(tmp, tmp)
gf127.Add(c01, tmp, c01)
*tmp = *c10
gf127.Mul10(c10, c10)
gf127.Add(c10, c11, c10)
gf127.Mul11(tmp, tmp)
gf127.Add(c11, tmp, c11)
} else {
*tmp = *c00
gf127.Mul10(c00, c00)
gf127.Add(c00, c01, c00)
*c01 = *tmp
*tmp = *c10
gf127.Mul10(c10, c10)
gf127.Add(c10, c11, c10)
*c11 = *tmp
}
}

View file

@ -17,11 +17,13 @@ var id = sl2{
{GF127{0, 0}, GF127{1, 0}}, {GF127{0, 0}, GF127{1, 0}},
} }
// MarshalBinary implements encoding.BinaryMarshaler.
func (c *sl2) MarshalBinary() (data []byte, err error) { func (c *sl2) MarshalBinary() (data []byte, err error) {
s := c.ByteArray() s := c.ByteArray()
return s[:], nil return s[:], nil
} }
// UnmarshalBinary implements encoding.BinaryUnmarshaler.
func (c *sl2) UnmarshalBinary(data []byte) (err error) { func (c *sl2) UnmarshalBinary(data []byte) (err error) {
if len(data) != 64 { if len(data) != 64 {
return errors.New("data must be 64-bytes long") return errors.New("data must be 64-bytes long")
@ -113,6 +115,7 @@ func (c *sl2) MulB() *sl2 {
return c return c
} }
// Mul returns a * b in GL_2(GF(2^127))
func (c *sl2) Mul(a, b *sl2) *sl2 { func (c *sl2) Mul(a, b *sl2) *sl2 {
var x [4]GF127 var x [4]GF127
@ -158,16 +161,16 @@ func (c *sl2) String() string {
func (c *sl2) ByteArray() (b [Size]byte) { func (c *sl2) ByteArray() (b [Size]byte) {
t := c[0][0].ByteArray() t := c[0][0].ByteArray()
copy(b[:], t) copy(b[:], t[:])
t = c[0][1].ByteArray() t = c[0][1].ByteArray()
copy(b[16:], t) copy(b[16:], t[:])
t = c[1][0].ByteArray() t = c[1][0].ByteArray()
copy(b[32:], t) copy(b[32:], t[:])
t = c[1][1].ByteArray() t = c[1][1].ByteArray()
copy(b[48:], t) copy(b[48:], t[:])
return return
} }