Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit a2b84bc

Browse files
authored
Merge pull request #28 from pjbgf/optimisations
goasm: Implement amd64 optimised code
2 parents 3b4a158 + 95a7df9 commit a2b84bc

15 files changed

+2797
-61
lines changed

.github/workflows/build.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,7 @@ jobs:
2929
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
3030
with:
3131
go-version: ${{ matrix.go-version }}
32+
- name: Run Verify
33+
run: make verify
3234
- name: Run Cross Build
3335
run: make cross-build

Makefile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,11 @@ build-nocgo:
3030

3131
# Run cross-compilation to assure supported architectures.
3232
cross-build: build-arm build-arm64 build-nocgo
33+
34+
generate:
35+
go run sha1cdblock_amd64_asm.go -out sha1cdblock_amd64.s
36+
sed -i 's;&\samd64;&\n// +build !noasm,gc,amd64;g' sha1cdblock_amd64.s
37+
38+
verify: generate
39+
git diff --exit-code
40+
go vet ./...

go.mod

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
11
module github.com/pjbgf/sha1cd
22

3-
go 1.15
3+
go 1.19
4+
5+
require (
6+
github.com/mmcloughlin/avo v0.5.0 // indirect
7+
golang.org/x/mod v0.6.0 // indirect
8+
golang.org/x/sys v0.1.0 // indirect
9+
golang.org/x/tools v0.2.0 // indirect
10+
)

go.sum

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
github.com/mmcloughlin/avo v0.5.0 h1:nAco9/aI9Lg2kiuROBY6BhCI/z0t5jEvJfjWbL8qXLU=
2+
github.com/mmcloughlin/avo v0.5.0/go.mod h1:ChHFdoV7ql95Wi7vuq2YT1bwCJqiWdZrQ1im3VujLYM=
3+
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
4+
golang.org/x/arch v0.1.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
5+
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
6+
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
7+
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
8+
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
9+
golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I=
10+
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
11+
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
12+
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
13+
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
14+
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
15+
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
16+
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
17+
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
18+
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
19+
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
20+
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
21+
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
22+
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
23+
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
24+
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
25+
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
26+
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
27+
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
28+
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
29+
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
30+
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
31+
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
32+
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
33+
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
34+
golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
35+
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
36+
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
37+
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

internal/const.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ const (
3333
// SHA1 processes the input data in chunks. Each chunk contains 64 bytes.
3434
Chunk = 64
3535

36+
// The number of pre-step compression state to store.
37+
// Currently there are 3 pre-step compression states required: 0, 58, 65.
38+
PreStepState = 3
39+
3640
Magic = "shacd\x01"
3741
MarshaledSize = len(Magic) + 5*4 + Chunk + 8
3842
)

sha1cd.go

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ type digest struct {
3838
len uint64
3939

4040
// col defines whether a collision has been found.
41-
col bool
41+
col bool
42+
blockFunc func(dig *digest, p []byte)
4243
}
4344

4445
func (d *digest) MarshalBinary() ([]byte, error) {
@@ -127,6 +128,17 @@ func (d *digest) Reset() {
127128
func New() hash.Hash {
128129
d := new(digest)
129130

131+
d.blockFunc = block
132+
d.Reset()
133+
return d
134+
}
135+
136+
// NewGeneric is equivalent to New but uses the Go generic implementation,
137+
// avoiding any processor-specific optimizations.
138+
func NewGeneric() hash.Hash {
139+
d := new(digest)
140+
141+
d.blockFunc = blockGeneric
130142
d.Reset()
131143
return d
132144
}
@@ -146,14 +158,14 @@ func (d *digest) Write(p []byte) (nn int, err error) {
146158
n := copy(d.x[d.nx:], p)
147159
d.nx += n
148160
if d.nx == shared.Chunk {
149-
block(d, d.x[:])
161+
d.blockFunc(d, d.x[:])
150162
d.nx = 0
151163
}
152164
p = p[n:]
153165
}
154166
if len(p) >= shared.Chunk {
155167
n := len(p) &^ (shared.Chunk - 1)
156-
block(d, p[:n])
168+
d.blockFunc(d, p[:n])
157169
p = p[n:]
158170
}
159171
if len(p) > 0 {

sha1cd_test.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -193,16 +193,25 @@ func TestLargeHashes(t *testing.T) {
193193
}
194194

195195
func TestAllocations(t *testing.T) {
196+
t.Run("generic", func(t *testing.T) {
197+
testAllocations(NewGeneric(), t)
198+
})
199+
200+
t.Run("native", func(t *testing.T) {
201+
testAllocations(New(), t)
202+
})
203+
}
204+
205+
func testAllocations(h hash.Hash, t *testing.T) {
196206
in := []byte("hello, world!")
197207
out := make([]byte, 0, Size)
198-
h := New()
199208
n := int(testing.AllocsPerRun(10, func() {
200209
h.Reset()
201210
h.Write(in)
202211
out = h.Sum(out[:0])
203212
}))
204213

205-
if n > 0 {
206-
t.Errorf("allocs = %d, want < 1", n)
214+
if n > 2 {
215+
t.Errorf("allocs = %d, want < 3", n)
207216
}
208217
}

sha1cdblock_amd64.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
//go:build !noasm && gc && amd64
2+
// +build !noasm,gc,amd64
3+
4+
package sha1cd
5+
6+
import (
7+
"math"
8+
"unsafe"
9+
10+
shared "github.com/pjbgf/sha1cd/internal"
11+
)
12+
13+
type sliceHeader struct {
14+
base uintptr
15+
len int
16+
cap int
17+
}
18+
19+
// blockAMD64 hashes the message p into the current state in dig.
20+
// Both m1 and cs are used to store intermediate results which are used by the collision detection logic.
21+
//
22+
//go:noescape
23+
func blockAMD64(dig *digest, p sliceHeader, m1 []uint32, cs [][5]uint32)
24+
25+
func block(dig *digest, p []byte) {
26+
m1 := [shared.Rounds]uint32{}
27+
cs := [shared.PreStepState][shared.WordBuffers]uint32{}
28+
29+
for len(p) >= shared.Chunk {
30+
// Only send a block to be processed, as the collission detection
31+
// works on a block by block basis.
32+
ips := sliceHeader{
33+
base: uintptr(unsafe.Pointer(&p[0])),
34+
len: int(math.Min(float64(len(p)), float64(shared.Chunk))),
35+
cap: shared.Chunk,
36+
}
37+
38+
blockAMD64(dig, ips, m1[:], cs[:])
39+
40+
col := checkCollision(m1, cs, dig.h)
41+
if col {
42+
dig.col = true
43+
44+
blockAMD64(dig, ips, m1[:], cs[:])
45+
blockAMD64(dig, ips, m1[:], cs[:])
46+
}
47+
48+
p = p[shared.Chunk:]
49+
}
50+
}

0 commit comments

Comments
 (0)