aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2023-04-02 21:21:24 -0700
committerGopher Robot <gobot@golang.org>2023-04-25 22:05:33 +0000
commitbb079efbdcc738a236b30f295ccb286df58e2bc3 (patch)
tree7cbefc3b2e9f8d315327923d3b135482bfbc16d9 /src
parentce0b9143123abe8005a059c92dbd763f7ab46214 (diff)
downloadgo-bb079efbdcc738a236b30f295ccb286df58e2bc3.tar.xz
crypto/sha256: add WriteString and WriteByte method
This can reduce allocations when hashing a string or byte rather than []byte. For #38776 Change-Id: I1c6dd1bc018220784a05939e92b47558c0562110 Reviewed-on: https://go-review.googlesource.com/c/go/+/481478 Reviewed-by: Joel Sing <joel@sing.id.au> Run-TryBot: Ian Lance Taylor <iant@golang.org> TryBot-Result: Gopher Robot <gobot@golang.org> Auto-Submit: Ian Lance Taylor <iant@google.com> Reviewed-by: Ian Lance Taylor <iant@google.com> Reviewed-by: Bryan Mills <bcmills@google.com> Run-TryBot: Ian Lance Taylor <iant@google.com>
Diffstat (limited to 'src')
-rw-r--r--src/crypto/internal/boring/sha.go14
-rw-r--r--src/crypto/sha256/sha256.go59
-rw-r--r--src/crypto/sha256/sha256_test.go33
-rw-r--r--src/crypto/sha256/sha256block.go2
-rw-r--r--src/crypto/sha256/sha256block_386.s6
-rw-r--r--src/crypto/sha256/sha256block_amd64.s14
-rw-r--r--src/crypto/sha256/sha256block_arm64.go18
-rw-r--r--src/crypto/sha256/sha256block_arm64.s8
-rw-r--r--src/crypto/sha256/sha256block_decl.go12
-rw-r--r--src/crypto/sha256/sha256block_generic.go4
-rw-r--r--src/crypto/sha256/sha256block_ppc64x.s8
-rw-r--r--src/crypto/sha256/sha256block_s390x.go9
-rw-r--r--src/crypto/sha256/sha256block_s390x.s8
13 files changed, 157 insertions, 38 deletions
diff --git a/src/crypto/internal/boring/sha.go b/src/crypto/internal/boring/sha.go
index cf82f3f64f..b7843674a5 100644
--- a/src/crypto/internal/boring/sha.go
+++ b/src/crypto/internal/boring/sha.go
@@ -263,6 +263,20 @@ func (h *sha256Hash) Write(p []byte) (int, error) {
return len(p), nil
}
+func (h *sha256Hash) WriteString(s string) (int, error) {
+ if len(s) > 0 && C._goboringcrypto_SHA256_Update(h.noescapeCtx(), unsafe.Pointer(unsafe.StringData(s)), C.size_t(len(s))) == 0 {
+ panic("boringcrypto: SHA256_Update failed")
+ }
+ return len(s), nil
+}
+
+func (h *sha256Hash) WriteByte(c byte) error {
+ if C._goboringcrypto_SHA256_Update(h.noescapeCtx(), unsafe.Pointer(&c), 1) == 0 {
+ panic("boringcrypto: SHA256_Update failed")
+ }
+ return nil
+}
+
func (h0 *sha256Hash) sum(dst []byte) []byte {
h := *h0 // make copy so future Write+Sum is valid
if C._goboringcrypto_SHA256_Final((*C.uint8_t)(noescape(unsafe.Pointer(&h.out[0]))), h.noescapeCtx()) == 0 {
diff --git a/src/crypto/sha256/sha256.go b/src/crypto/sha256/sha256.go
index 2deafbc9fc..56295452e2 100644
--- a/src/crypto/sha256/sha256.go
+++ b/src/crypto/sha256/sha256.go
@@ -177,21 +177,35 @@ func (d *digest) Size() int {
func (d *digest) BlockSize() int { return BlockSize }
func (d *digest) Write(p []byte) (nn int, err error) {
- boring.Unreachable()
nn = len(p)
d.len += uint64(nn)
- if d.nx > 0 {
- n := copy(d.x[d.nx:], p)
- d.nx += n
- if d.nx == chunk {
- block(d, d.x[:])
- d.nx = 0
- }
+ n := fillChunk(d, p)
+ p = p[n:]
+ if len(p) >= chunk {
+ n := len(p) &^ (chunk - 1)
+ block(d, p[:n])
p = p[n:]
}
+ if len(p) > 0 {
+ d.nx = copy(d.x[:], p)
+ }
+ return
+}
+
+func (d *digest) WriteString(p string) (nn int, err error) {
+ nn = len(p)
+ d.len += uint64(nn)
+ n := fillChunk(d, p)
+
+ // This duplicates the code in Write, except that it calls
+ // blockString rather than block. It would be nicer to pass
+ // in a func, but as of this writing (Go 1.20) that causes
+ // memory allocations that we want to avoid.
+
+ p = p[n:]
if len(p) >= chunk {
n := len(p) &^ (chunk - 1)
- block(d, p[:n])
+ blockString(d, p[:n])
p = p[n:]
}
if len(p) > 0 {
@@ -200,6 +214,33 @@ func (d *digest) Write(p []byte) (nn int, err error) {
return
}
+// fillChunk fills the remainder of the current chunk, if any.
+func fillChunk[S []byte | string](d *digest, p S) int {
+ boring.Unreachable()
+ if d.nx == 0 {
+ return 0
+ }
+ n := copy(d.x[d.nx:], p)
+ d.nx += n
+ if d.nx == chunk {
+ block(d, d.x[:])
+ d.nx = 0
+ }
+ return n
+}
+
+func (d *digest) WriteByte(c byte) error {
+ boring.Unreachable()
+ d.len++
+ d.x[d.nx] = c
+ d.nx++
+ if d.nx == chunk {
+ block(d, d.x[:])
+ d.nx = 0
+ }
+ return nil
+}
+
func (d *digest) Sum(in []byte) []byte {
boring.Unreachable()
// Make a copy of d so that caller can keep writing and summing.
diff --git a/src/crypto/sha256/sha256_test.go b/src/crypto/sha256/sha256_test.go
index 7304678346..90353c467e 100644
--- a/src/crypto/sha256/sha256_test.go
+++ b/src/crypto/sha256/sha256_test.go
@@ -113,6 +113,14 @@ func TestGolden(t *testing.T) {
}
c.Reset()
}
+ bw := c.(io.ByteWriter)
+ for i := 0; i < len(g.in); i++ {
+ bw.WriteByte(g.in[i])
+ }
+ s = fmt.Sprintf("%x", c.Sum(nil))
+ if s != g.out {
+ t.Errorf("sha256[WriteByte](%s) = %s want %s", g.in, s, g.out)
+ }
}
for i := 0; i < len(golden224); i++ {
g := golden224[i]
@@ -297,7 +305,8 @@ func TestAllocations(t *testing.T) {
if boring.Enabled {
t.Skip("BoringCrypto doesn't allocate the same way as stdlib")
}
- in := []byte("hello, world!")
+ const ins = "hello, world!"
+ in := []byte(ins)
out := make([]byte, 0, Size)
h := New()
n := int(testing.AllocsPerRun(10, func() {
@@ -308,6 +317,28 @@ func TestAllocations(t *testing.T) {
if n > 0 {
t.Errorf("allocs = %d, want 0", n)
}
+
+ sw := h.(io.StringWriter)
+ n = int(testing.AllocsPerRun(10, func() {
+ h.Reset()
+ sw.WriteString(ins)
+ out = h.Sum(out[:0])
+ }))
+ if n > 0 {
+ t.Errorf("string allocs = %d, want 0", n)
+ }
+
+ bw := h.(io.ByteWriter)
+ n = int(testing.AllocsPerRun(10, func() {
+ h.Reset()
+ for _, b := range in {
+ bw.WriteByte(b)
+ }
+ out = h.Sum(out[:0])
+ }))
+ if n > 0 {
+ t.Errorf("byte allocs = %d, want 0", n)
+ }
}
type cgoData struct {
diff --git a/src/crypto/sha256/sha256block.go b/src/crypto/sha256/sha256block.go
index bd2f9da93c..418bf0ce29 100644
--- a/src/crypto/sha256/sha256block.go
+++ b/src/crypto/sha256/sha256block.go
@@ -77,7 +77,7 @@ var _K = []uint32{
0xc67178f2,
}
-func blockGeneric(dig *digest, p []byte) {
+func blockGeneric[S []byte | string](dig *digest, p S) {
var w [64]uint32
h0, h1, h2, h3, h4, h5, h6, h7 := dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4], dig.h[5], dig.h[6], dig.h[7]
for len(p) >= chunk {
diff --git a/src/crypto/sha256/sha256block_386.s b/src/crypto/sha256/sha256block_386.s
index 086a0ab25c..ad23491d02 100644
--- a/src/crypto/sha256/sha256block_386.s
+++ b/src/crypto/sha256/sha256block_386.s
@@ -141,9 +141,9 @@
MSGSCHEDULE1(index); \
SHA256ROUND(index, const, a, b, c, d, e, f, g, h)
-TEXT ·block(SB),0,$296-16
- MOVL p_base+4(FP), SI
- MOVL p_len+8(FP), DX
+TEXT ·doBlock(SB),0,$296-12
+ MOVL p+4(FP), SI
+ MOVL n+8(FP), DX
SHRL $6, DX
SHLL $6, DX
diff --git a/src/crypto/sha256/sha256block_amd64.s b/src/crypto/sha256/sha256block_amd64.s
index 03535fb51c..6a028c6f53 100644
--- a/src/crypto/sha256/sha256block_amd64.s
+++ b/src/crypto/sha256/sha256block_amd64.s
@@ -619,14 +619,14 @@
SHA256RNDS2 msg, state1, state0 \
sha256Msg1 (m,a)
-TEXT ·block(SB), 0, $536-32
+TEXT ·doBlock(SB), 0, $536-24
CMPB ·useSHA(SB), $1
JE sha_ni
CMPB ·useAVX2(SB), $1
JE avx2
- MOVQ p_base+8(FP), SI
- MOVQ p_len+16(FP), DX
+ MOVQ p+8(FP), SI
+ MOVQ n+16(FP), DX
SHRQ $6, DX
SHLQ $6, DX
@@ -741,8 +741,8 @@ end:
avx2:
MOVQ dig+0(FP), CTX // d.h[8]
- MOVQ p_base+8(FP), INP
- MOVQ p_len+16(FP), NUM_BYTES
+ MOVQ p+8(FP), INP
+ MOVQ n+16(FP), NUM_BYTES
LEAQ -64(INP)(NUM_BYTES*1), NUM_BYTES // Pointer to the last block
MOVQ NUM_BYTES, _INP_END(SP)
@@ -935,8 +935,8 @@ done_hash:
sha_ni:
MOVQ dig+0(FP), digestPtr // init digest hash vector H0, H1,..., H7 pointer
- MOVQ p_base+8(FP), dataPtr // init input data base pointer
- MOVQ p_len+16(FP), numBytes // get number of input bytes to hash
+ MOVQ p+8(FP), dataPtr // init input data base pointer
+ MOVQ n+16(FP), numBytes // get number of input bytes to hash
SHRQ $6, numBytes // force modulo 64 input buffer length
SHLQ $6, numBytes
CMPQ numBytes, $0 // exit early for zero-length input buffer
diff --git a/src/crypto/sha256/sha256block_arm64.go b/src/crypto/sha256/sha256block_arm64.go
index e5da566363..a2f1a2ba76 100644
--- a/src/crypto/sha256/sha256block_arm64.go
+++ b/src/crypto/sha256/sha256block_arm64.go
@@ -4,18 +4,30 @@
package sha256
-import "internal/cpu"
+import (
+ "internal/cpu"
+ "unsafe"
+)
var k = _K
//go:noescape
-func sha256block(h []uint32, p []byte, k []uint32)
+func sha256block(h []uint32, p *byte, n int, k []uint32)
func block(dig *digest, p []byte) {
if !cpu.ARM64.HasSHA2 {
blockGeneric(dig, p)
} else {
h := dig.h[:]
- sha256block(h, p, k)
+ sha256block(h, unsafe.SliceData(p), len(p), k)
+ }
+}
+
+func blockString(dig *digest, s string) {
+ if !cpu.ARM64.HasSHA2 {
+ blockGeneric(dig, s)
+ } else {
+ h := dig.h[:]
+ sha256block(h, unsafe.StringData(s), len(s), k)
}
}
diff --git a/src/crypto/sha256/sha256block_arm64.s b/src/crypto/sha256/sha256block_arm64.s
index d5c1eb0b2e..040f162ac4 100644
--- a/src/crypto/sha256/sha256block_arm64.s
+++ b/src/crypto/sha256/sha256block_arm64.s
@@ -9,12 +9,12 @@
SHA256H2 V9.S4, V8, V3 \
VMOV V2.B16, V8.B16
-// func sha256block(h []uint32, p []byte, k []uint32)
+// func sha256block(h []uint32, p *byte, n int, k []uint32)
TEXT ·sha256block(SB),NOSPLIT,$0
MOVD h_base+0(FP), R0 // Hash value first address
- MOVD p_base+24(FP), R1 // message first address
- MOVD k_base+48(FP), R2 // k constants first address
- MOVD p_len+32(FP), R3 // message length
+ MOVD p+24(FP), R1 // message first address
+ MOVD k_base+40(FP), R2 // k constants first address
+ MOVD n+32(FP), R3 // message length
VLD1 (R0), [V0.S4, V1.S4] // load h(a,b,c,d,e,f,g,h)
VLD1.P 64(R2), [V16.S4, V17.S4, V18.S4, V19.S4]
VLD1.P 64(R2), [V20.S4, V21.S4, V22.S4, V23.S4]
diff --git a/src/crypto/sha256/sha256block_decl.go b/src/crypto/sha256/sha256block_decl.go
index 7d68cd95fe..28834a2972 100644
--- a/src/crypto/sha256/sha256block_decl.go
+++ b/src/crypto/sha256/sha256block_decl.go
@@ -6,5 +6,15 @@
package sha256
+import "unsafe"
+
//go:noescape
-func block(dig *digest, p []byte)
+func doBlock(dig *digest, p *byte, n int)
+
+func block(dig *digest, p []byte) {
+ doBlock(dig, unsafe.SliceData(p), len(p))
+}
+
+func blockString(dig *digest, s string) {
+ doBlock(dig, unsafe.StringData(s), len(s))
+}
diff --git a/src/crypto/sha256/sha256block_generic.go b/src/crypto/sha256/sha256block_generic.go
index fd098bec89..2240c0087e 100644
--- a/src/crypto/sha256/sha256block_generic.go
+++ b/src/crypto/sha256/sha256block_generic.go
@@ -9,3 +9,7 @@ package sha256
func block(dig *digest, p []byte) {
blockGeneric(dig, p)
}
+
+func blockString(dig *digest, s string) {
+ blockGeneric(dig, s)
+}
diff --git a/src/crypto/sha256/sha256block_ppc64x.s b/src/crypto/sha256/sha256block_ppc64x.s
index b229ef619a..b238b57cc3 100644
--- a/src/crypto/sha256/sha256block_ppc64x.s
+++ b/src/crypto/sha256/sha256block_ppc64x.s
@@ -284,11 +284,11 @@ GLOBL ·kcon(SB), RODATA, $1088
#define VPERMLE(va,vb,vc,vt)
#endif
-// func block(dig *digest, p []byte)
-TEXT ·block(SB),0,$0-32
+// func doBlock(dig *digest, p []byte)
+TEXT ·doBlock(SB),0,$0-24
MOVD dig+0(FP), CTX
- MOVD p_base+8(FP), INP
- MOVD p_len+16(FP), LEN
+ MOVD p+8(FP), INP
+ MOVD n+16(FP), LEN
SRD $6, LEN
SLD $6, LEN
diff --git a/src/crypto/sha256/sha256block_s390x.go b/src/crypto/sha256/sha256block_s390x.go
index 1a376c5f93..2887c12c50 100644
--- a/src/crypto/sha256/sha256block_s390x.go
+++ b/src/crypto/sha256/sha256block_s390x.go
@@ -4,6 +4,13 @@
package sha256
-import "internal/cpu"
+import (
+ "internal/cpu"
+ "unsafe"
+)
var useAsm = cpu.S390X.HasSHA256
+
+func doBlockGeneric(dig *digest, p *byte, n int) {
+ blockGeneric(dig, unsafe.String(p, n))
+}
diff --git a/src/crypto/sha256/sha256block_s390x.s b/src/crypto/sha256/sha256block_s390x.s
index 81b1b382c7..8f83a9426b 100644
--- a/src/crypto/sha256/sha256block_s390x.s
+++ b/src/crypto/sha256/sha256block_s390x.s
@@ -4,10 +4,10 @@
#include "textflag.h"
-// func block(dig *digest, p []byte)
-TEXT ·block(SB), NOSPLIT|NOFRAME, $0-32
+// func doBlock(dig *digest, p *byte, n int)
+TEXT ·doBlock(SB), NOSPLIT|NOFRAME, $0-24
MOVBZ ·useAsm(SB), R4
- LMG dig+0(FP), R1, R3 // R2 = &p[0], R3 = len(p)
+ LMG dig+0(FP), R1, R3 // R2 = p, R3 = n
MOVBZ $2, R0 // SHA-256 function code
CMPBEQ R4, $0, generic
@@ -17,4 +17,4 @@ loop:
RET
generic:
- BR ·blockGeneric(SB)
+ BR ·doBlockGeneric(SB)