"func @\"\".slicebytetostringtmp (? []byte) (? string)\n"
"func @\"\".slicerunetostring (? []rune) (? string)\n"
"func @\"\".stringtoslicebyte (? string) (? []byte)\n"
+ "func @\"\".stringtoslicebytetmp (? string) (? []byte)\n"
"func @\"\".stringtoslicerune (? string) (? []rune)\n"
"func @\"\".stringiter (? string, ? int) (? int)\n"
"func @\"\".stringiter2 (? string, ? int) (@\"\".retkĀ·1 int, @\"\".retvĀ·2 rune)\n"
OARRAYBYTESTRTMP, // string(bytes) ephemeral
OARRAYRUNESTR, // string(runes)
OSTRARRAYBYTE, // []byte(s)
+ OSTRARRAYBYTETMP, // []byte(s) ephemeral
OSTRARRAYRUNE, // []rune(s)
OAS, // x = y or x := y
OAS2, // x, y, z = xx, yy, zz
default:
fatal("orderstmt range %T", n->type);
case TARRAY:
+ // Mark []byte(str) range expression to reuse string backing storage.
+ // It is safe because the storage cannot be mutated.
+ if(n->right->op == OSTRARRAYBYTE)
+ n->right->op = OSTRARRAYBYTETMP;
if(count(n->list) < 2 || isblank(n->list->next->n)) {
// for i := range x will only use x once, to compute len(x).
// No need to copy it.
func slicebytetostringtmp([]byte) string
func slicerunetostring([]rune) string
func stringtoslicebyte(string) []byte
+func stringtoslicebytetmp(string) []byte
func stringtoslicerune(string) []rune
func stringiter(string, int) int
func stringiter2(string, int) (retk int, retv rune)
n = mkcall("stringtoslicebyte", n->type, init, conv(n->left, types[TSTRING]));
goto ret;
+ case OSTRARRAYBYTETMP:
+ // stringtoslicebytetmp(string) []byte;
+ n = mkcall("stringtoslicebytetmp", n->type, init, conv(n->left, types[TSTRING]));
+ goto ret;
+
case OSTRARRAYRUNE:
// stringtoslicerune(string) []rune
n = mkcall("stringtoslicerune", n->type, init, n->left);
return b
}
+func stringtoslicebytetmp(s string) []byte {
+ // Return a slice referring to the actual string bytes.
+ // This is only for use by internal compiler optimizations
+ // that know that the slice won't be mutated.
+ // The only such case today is:
+ // for i, c := range []byte(str)
+
+ str := (*stringStruct)(unsafe.Pointer(&s))
+ ret := slice{array: (*byte)(str.str), len: uint(str.len), cap: uint(str.len)}
+ return *(*[]byte)(unsafe.Pointer(&ret))
+}
+
func stringtoslicerune(s string) []rune {
// two passes.
// unlike slicerunetostring, no race because strings are immutable.
t.Fatalf("want 0 allocs, got %v", n)
}
}
+
+func TestRangeStringCast(t *testing.T) {
+ s := "abc"
+ n := testing.AllocsPerRun(1000, func() {
+ for i, c := range []byte(s) {
+ if c != s[i] {
+ t.Fatalf("want '%c' at pos %v, got '%c'", s[i], i, c)
+ }
+ }
+ })
+ if n != 0 {
+ t.Fatalf("want 0 allocs, got %v", n)
+ }
+}