mirror of https://github.com/golang/go.git
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>
This commit is contained in:
parent
ce0b914312
commit
bb079efbdc
|
|
@ -263,6 +263,20 @@ func (h *sha256Hash) Write(p []byte) (int, error) {
|
||||||
return len(p), nil
|
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 {
|
func (h0 *sha256Hash) sum(dst []byte) []byte {
|
||||||
h := *h0 // make copy so future Write+Sum is valid
|
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 {
|
if C._goboringcrypto_SHA256_Final((*C.uint8_t)(noescape(unsafe.Pointer(&h.out[0]))), h.noescapeCtx()) == 0 {
|
||||||
|
|
|
||||||
|
|
@ -177,18 +177,10 @@ func (d *digest) Size() int {
|
||||||
func (d *digest) BlockSize() int { return BlockSize }
|
func (d *digest) BlockSize() int { return BlockSize }
|
||||||
|
|
||||||
func (d *digest) Write(p []byte) (nn int, err error) {
|
func (d *digest) Write(p []byte) (nn int, err error) {
|
||||||
boring.Unreachable()
|
|
||||||
nn = len(p)
|
nn = len(p)
|
||||||
d.len += uint64(nn)
|
d.len += uint64(nn)
|
||||||
if d.nx > 0 {
|
n := fillChunk(d, p)
|
||||||
n := copy(d.x[d.nx:], p)
|
|
||||||
d.nx += n
|
|
||||||
if d.nx == chunk {
|
|
||||||
block(d, d.x[:])
|
|
||||||
d.nx = 0
|
|
||||||
}
|
|
||||||
p = p[n:]
|
p = p[n:]
|
||||||
}
|
|
||||||
if len(p) >= chunk {
|
if len(p) >= chunk {
|
||||||
n := len(p) &^ (chunk - 1)
|
n := len(p) &^ (chunk - 1)
|
||||||
block(d, p[:n])
|
block(d, p[:n])
|
||||||
|
|
@ -200,6 +192,55 @@ func (d *digest) Write(p []byte) (nn int, err error) {
|
||||||
return
|
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)
|
||||||
|
blockString(d, p[:n])
|
||||||
|
p = p[n:]
|
||||||
|
}
|
||||||
|
if len(p) > 0 {
|
||||||
|
d.nx = copy(d.x[:], p)
|
||||||
|
}
|
||||||
|
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 {
|
func (d *digest) Sum(in []byte) []byte {
|
||||||
boring.Unreachable()
|
boring.Unreachable()
|
||||||
// Make a copy of d so that caller can keep writing and summing.
|
// Make a copy of d so that caller can keep writing and summing.
|
||||||
|
|
|
||||||
|
|
@ -113,6 +113,14 @@ func TestGolden(t *testing.T) {
|
||||||
}
|
}
|
||||||
c.Reset()
|
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++ {
|
for i := 0; i < len(golden224); i++ {
|
||||||
g := golden224[i]
|
g := golden224[i]
|
||||||
|
|
@ -297,7 +305,8 @@ func TestAllocations(t *testing.T) {
|
||||||
if boring.Enabled {
|
if boring.Enabled {
|
||||||
t.Skip("BoringCrypto doesn't allocate the same way as stdlib")
|
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)
|
out := make([]byte, 0, Size)
|
||||||
h := New()
|
h := New()
|
||||||
n := int(testing.AllocsPerRun(10, func() {
|
n := int(testing.AllocsPerRun(10, func() {
|
||||||
|
|
@ -308,6 +317,28 @@ func TestAllocations(t *testing.T) {
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
t.Errorf("allocs = %d, want 0", n)
|
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 {
|
type cgoData struct {
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ var _K = []uint32{
|
||||||
0xc67178f2,
|
0xc67178f2,
|
||||||
}
|
}
|
||||||
|
|
||||||
func blockGeneric(dig *digest, p []byte) {
|
func blockGeneric[S []byte | string](dig *digest, p S) {
|
||||||
var w [64]uint32
|
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]
|
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 {
|
for len(p) >= chunk {
|
||||||
|
|
|
||||||
|
|
@ -141,9 +141,9 @@
|
||||||
MSGSCHEDULE1(index); \
|
MSGSCHEDULE1(index); \
|
||||||
SHA256ROUND(index, const, a, b, c, d, e, f, g, h)
|
SHA256ROUND(index, const, a, b, c, d, e, f, g, h)
|
||||||
|
|
||||||
TEXT ·block(SB),0,$296-16
|
TEXT ·doBlock(SB),0,$296-12
|
||||||
MOVL p_base+4(FP), SI
|
MOVL p+4(FP), SI
|
||||||
MOVL p_len+8(FP), DX
|
MOVL n+8(FP), DX
|
||||||
SHRL $6, DX
|
SHRL $6, DX
|
||||||
SHLL $6, DX
|
SHLL $6, DX
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -619,14 +619,14 @@
|
||||||
SHA256RNDS2 msg, state1, state0 \
|
SHA256RNDS2 msg, state1, state0 \
|
||||||
sha256Msg1 (m,a)
|
sha256Msg1 (m,a)
|
||||||
|
|
||||||
TEXT ·block(SB), 0, $536-32
|
TEXT ·doBlock(SB), 0, $536-24
|
||||||
CMPB ·useSHA(SB), $1
|
CMPB ·useSHA(SB), $1
|
||||||
JE sha_ni
|
JE sha_ni
|
||||||
CMPB ·useAVX2(SB), $1
|
CMPB ·useAVX2(SB), $1
|
||||||
JE avx2
|
JE avx2
|
||||||
|
|
||||||
MOVQ p_base+8(FP), SI
|
MOVQ p+8(FP), SI
|
||||||
MOVQ p_len+16(FP), DX
|
MOVQ n+16(FP), DX
|
||||||
SHRQ $6, DX
|
SHRQ $6, DX
|
||||||
SHLQ $6, DX
|
SHLQ $6, DX
|
||||||
|
|
||||||
|
|
@ -741,8 +741,8 @@ end:
|
||||||
|
|
||||||
avx2:
|
avx2:
|
||||||
MOVQ dig+0(FP), CTX // d.h[8]
|
MOVQ dig+0(FP), CTX // d.h[8]
|
||||||
MOVQ p_base+8(FP), INP
|
MOVQ p+8(FP), INP
|
||||||
MOVQ p_len+16(FP), NUM_BYTES
|
MOVQ n+16(FP), NUM_BYTES
|
||||||
|
|
||||||
LEAQ -64(INP)(NUM_BYTES*1), NUM_BYTES // Pointer to the last block
|
LEAQ -64(INP)(NUM_BYTES*1), NUM_BYTES // Pointer to the last block
|
||||||
MOVQ NUM_BYTES, _INP_END(SP)
|
MOVQ NUM_BYTES, _INP_END(SP)
|
||||||
|
|
@ -935,8 +935,8 @@ done_hash:
|
||||||
|
|
||||||
sha_ni:
|
sha_ni:
|
||||||
MOVQ dig+0(FP), digestPtr // init digest hash vector H0, H1,..., H7 pointer
|
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+8(FP), dataPtr // init input data base pointer
|
||||||
MOVQ p_len+16(FP), numBytes // get number of input bytes to hash
|
MOVQ n+16(FP), numBytes // get number of input bytes to hash
|
||||||
SHRQ $6, numBytes // force modulo 64 input buffer length
|
SHRQ $6, numBytes // force modulo 64 input buffer length
|
||||||
SHLQ $6, numBytes
|
SHLQ $6, numBytes
|
||||||
CMPQ numBytes, $0 // exit early for zero-length input buffer
|
CMPQ numBytes, $0 // exit early for zero-length input buffer
|
||||||
|
|
|
||||||
|
|
@ -4,18 +4,30 @@
|
||||||
|
|
||||||
package sha256
|
package sha256
|
||||||
|
|
||||||
import "internal/cpu"
|
import (
|
||||||
|
"internal/cpu"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
var k = _K
|
var k = _K
|
||||||
|
|
||||||
//go:noescape
|
//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) {
|
func block(dig *digest, p []byte) {
|
||||||
if !cpu.ARM64.HasSHA2 {
|
if !cpu.ARM64.HasSHA2 {
|
||||||
blockGeneric(dig, p)
|
blockGeneric(dig, p)
|
||||||
} else {
|
} else {
|
||||||
h := dig.h[:]
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,12 @@
|
||||||
SHA256H2 V9.S4, V8, V3 \
|
SHA256H2 V9.S4, V8, V3 \
|
||||||
VMOV V2.B16, V8.B16
|
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
|
TEXT ·sha256block(SB),NOSPLIT,$0
|
||||||
MOVD h_base+0(FP), R0 // Hash value first address
|
MOVD h_base+0(FP), R0 // Hash value first address
|
||||||
MOVD p_base+24(FP), R1 // message first address
|
MOVD p+24(FP), R1 // message first address
|
||||||
MOVD k_base+48(FP), R2 // k constants first address
|
MOVD k_base+40(FP), R2 // k constants first address
|
||||||
MOVD p_len+32(FP), R3 // message length
|
MOVD n+32(FP), R3 // message length
|
||||||
VLD1 (R0), [V0.S4, V1.S4] // load h(a,b,c,d,e,f,g,h)
|
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), [V16.S4, V17.S4, V18.S4, V19.S4]
|
||||||
VLD1.P 64(R2), [V20.S4, V21.S4, V22.S4, V23.S4]
|
VLD1.P 64(R2), [V20.S4, V21.S4, V22.S4, V23.S4]
|
||||||
|
|
|
||||||
|
|
@ -6,5 +6,15 @@
|
||||||
|
|
||||||
package sha256
|
package sha256
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
//go:noescape
|
//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))
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,3 +9,7 @@ package sha256
|
||||||
func block(dig *digest, p []byte) {
|
func block(dig *digest, p []byte) {
|
||||||
blockGeneric(dig, p)
|
blockGeneric(dig, p)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func blockString(dig *digest, s string) {
|
||||||
|
blockGeneric(dig, s)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -284,11 +284,11 @@ GLOBL ·kcon(SB), RODATA, $1088
|
||||||
#define VPERMLE(va,vb,vc,vt)
|
#define VPERMLE(va,vb,vc,vt)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// func block(dig *digest, p []byte)
|
// func doBlock(dig *digest, p []byte)
|
||||||
TEXT ·block(SB),0,$0-32
|
TEXT ·doBlock(SB),0,$0-24
|
||||||
MOVD dig+0(FP), CTX
|
MOVD dig+0(FP), CTX
|
||||||
MOVD p_base+8(FP), INP
|
MOVD p+8(FP), INP
|
||||||
MOVD p_len+16(FP), LEN
|
MOVD n+16(FP), LEN
|
||||||
|
|
||||||
SRD $6, LEN
|
SRD $6, LEN
|
||||||
SLD $6, LEN
|
SLD $6, LEN
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,13 @@
|
||||||
|
|
||||||
package sha256
|
package sha256
|
||||||
|
|
||||||
import "internal/cpu"
|
import (
|
||||||
|
"internal/cpu"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
var useAsm = cpu.S390X.HasSHA256
|
var useAsm = cpu.S390X.HasSHA256
|
||||||
|
|
||||||
|
func doBlockGeneric(dig *digest, p *byte, n int) {
|
||||||
|
blockGeneric(dig, unsafe.String(p, n))
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,10 @@
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
// func block(dig *digest, p []byte)
|
// func doBlock(dig *digest, p *byte, n int)
|
||||||
TEXT ·block(SB), NOSPLIT|NOFRAME, $0-32
|
TEXT ·doBlock(SB), NOSPLIT|NOFRAME, $0-24
|
||||||
MOVBZ ·useAsm(SB), R4
|
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
|
MOVBZ $2, R0 // SHA-256 function code
|
||||||
CMPBEQ R4, $0, generic
|
CMPBEQ R4, $0, generic
|
||||||
|
|
||||||
|
|
@ -17,4 +17,4 @@ loop:
|
||||||
RET
|
RET
|
||||||
|
|
||||||
generic:
|
generic:
|
||||||
BR ·blockGeneric(SB)
|
BR ·doBlockGeneric(SB)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue