--- /dev/null
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package test
+
+import (
+ "crypto/sha256"
+ "runtime"
+ "testing"
+)
+
+func Verify(token, salt string) [32]byte {
+ return sha256.Sum256([]byte(token + salt))
+}
+
+func TestIssue71943(t *testing.T) {
+ if n := testing.AllocsPerRun(10, func() {
+ runtime.KeepAlive(Verify("teststring", "test"))
+ }); n > 0 {
+ t.Fatalf("unexpected allocation: %f", n)
+ }
+}
func concatstring5(*[32]byte, string, string, string, string, string) string
func concatstrings(*[32]byte, []string) string
-func concatbyte2(string, string) []byte
-func concatbyte3(string, string, string) []byte
-func concatbyte4(string, string, string, string) []byte
-func concatbyte5(string, string, string, string, string) []byte
-func concatbytes([]string) []byte
+func concatbyte2(*[32]byte, string, string) []byte
+func concatbyte3(*[32]byte, string, string, string) []byte
+func concatbyte4(*[32]byte, string, string, string, string) []byte
+func concatbyte5(*[32]byte, string, string, string, string, string) []byte
+func concatbytes(*[32]byte, []string) []byte
func cmpstring(string, string) int
func intstring(*[4]byte, int64) string
typs[38] = types.NewSlice(typs[28])
typs[39] = newSig(params(typs[33], typs[38]), params(typs[28]))
typs[40] = types.NewSlice(typs[0])
- typs[41] = newSig(params(typs[28], typs[28]), params(typs[40]))
- typs[42] = newSig(params(typs[28], typs[28], typs[28]), params(typs[40]))
- typs[43] = newSig(params(typs[28], typs[28], typs[28], typs[28]), params(typs[40]))
- typs[44] = newSig(params(typs[28], typs[28], typs[28], typs[28], typs[28]), params(typs[40]))
- typs[45] = newSig(params(typs[38]), params(typs[40]))
+ typs[41] = newSig(params(typs[33], typs[28], typs[28]), params(typs[40]))
+ typs[42] = newSig(params(typs[33], typs[28], typs[28], typs[28]), params(typs[40]))
+ typs[43] = newSig(params(typs[33], typs[28], typs[28], typs[28], typs[28]), params(typs[40]))
+ typs[44] = newSig(params(typs[33], typs[28], typs[28], typs[28], typs[28], typs[28]), params(typs[40]))
+ typs[45] = newSig(params(typs[33], typs[38]), params(typs[40]))
typs[46] = newSig(params(typs[28], typs[28]), params(typs[15]))
typs[47] = types.NewArray(typs[0], 4)
typs[48] = types.NewPtr(typs[47])
s := n.X
if expr, ok := s.(*ir.AddStringExpr); ok {
- return walkAddString(n.Type(), expr, init)
+ return walkAddString(expr, init, n)
}
if ir.IsConst(s, constant.String) {
return walkNew(n, init)
case ir.OADDSTR:
- return walkAddString(n.Type(), n.(*ir.AddStringExpr), init)
+ return walkAddString(n.(*ir.AddStringExpr), init, nil)
case ir.OAPPEND:
// order should make sure we only see OAS(node, OAPPEND), which we handle above.
return l
}
-func walkAddString(typ *types.Type, n *ir.AddStringExpr, init *ir.Nodes) ir.Node {
- c := len(n.List)
-
+// walkAddString walks a string concatenation expression x.
+// If conv is non nil, x is the conv.X field.
+func walkAddString(x *ir.AddStringExpr, init *ir.Nodes, conv *ir.ConvExpr) ir.Node {
+ c := len(x.List)
if c < 2 {
base.Fatalf("walkAddString count %d too small", c)
}
+ typ := x.Type()
+ if conv != nil {
+ typ = conv.Type()
+ }
+
// list of string arguments
var args []ir.Node
var fn, fnsmall, fnbig string
+ buf := typecheck.NodNil()
switch {
default:
- base.FatalfAt(n.Pos(), "unexpected type: %v", typ)
+ base.FatalfAt(x.Pos(), "unexpected type: %v", typ)
case typ.IsString():
- buf := typecheck.NodNil()
- if n.Esc() == ir.EscNone {
+ if x.Esc() == ir.EscNone {
sz := int64(0)
- for _, n1 := range n.List {
+ for _, n1 := range x.List {
if n1.Op() == ir.OLITERAL {
sz += int64(len(ir.StringVal(n1)))
}
args = []ir.Node{buf}
fnsmall, fnbig = "concatstring%d", "concatstrings"
case typ.IsSlice() && typ.Elem().IsKind(types.TUINT8): // Optimize []byte(str1+str2+...)
+ if conv != nil && conv.Esc() == ir.EscNone {
+ buf = stackBufAddr(tmpstringbufsize, types.Types[types.TUINT8])
+ }
+ args = []ir.Node{buf}
fnsmall, fnbig = "concatbyte%d", "concatbytes"
}
// note: order.expr knows this cutoff too.
fn = fmt.Sprintf(fnsmall, c)
- for _, n2 := range n.List {
+ for _, n2 := range x.List {
args = append(args, typecheck.Conv(n2, types.Types[types.TSTRING]))
}
} else {
fn = fnbig
t := types.NewSlice(types.Types[types.TSTRING])
- slargs := make([]ir.Node, len(n.List))
- for i, n2 := range n.List {
+ slargs := make([]ir.Node, len(x.List))
+ for i, n2 := range x.List {
slargs[i] = typecheck.Conv(n2, types.Types[types.TSTRING])
}
slice := ir.NewCompLitExpr(base.Pos, ir.OCOMPLIT, t, slargs)
- slice.Prealloc = n.Prealloc
+ slice.Prealloc = x.Prealloc
args = append(args, slice)
slice.SetEsc(ir.EscNone)
}
// concatbytes implements a Go string concatenation x+y+z+... returning a slice
// of bytes.
// The operands are passed in the slice a.
-func concatbytes(a []string) []byte {
+func concatbytes(buf *tmpBuf, a []string) []byte {
l := 0
for _, x := range a {
n := len(x)
return []byte{}
}
- b := rawbyteslice(l)
+ var b []byte
+ if buf != nil && l <= len(buf) {
+ *buf = tmpBuf{}
+ b = buf[:l]
+ } else {
+ b = rawbyteslice(l)
+ }
offset := 0
for _, x := range a {
copy(b[offset:], x)
return b
}
-func concatbyte2(a0, a1 string) []byte {
- return concatbytes([]string{a0, a1})
+func concatbyte2(buf *tmpBuf, a0, a1 string) []byte {
+ return concatbytes(buf, []string{a0, a1})
}
-func concatbyte3(a0, a1, a2 string) []byte {
- return concatbytes([]string{a0, a1, a2})
+func concatbyte3(buf *tmpBuf, a0, a1, a2 string) []byte {
+ return concatbytes(buf, []string{a0, a1, a2})
}
-func concatbyte4(a0, a1, a2, a3 string) []byte {
- return concatbytes([]string{a0, a1, a2, a3})
+func concatbyte4(buf *tmpBuf, a0, a1, a2, a3 string) []byte {
+ return concatbytes(buf, []string{a0, a1, a2, a3})
}
-func concatbyte5(a0, a1, a2, a3, a4 string) []byte {
- return concatbytes([]string{a0, a1, a2, a3, a4})
+func concatbyte5(buf *tmpBuf, a0, a1, a2, a3, a4 string) []byte {
+ return concatbytes(buf, []string{a0, a1, a2, a3, a4})
}
// slicebytetostring converts a byte slice to a string.