]> Cypherpunks repositories - gostls13.git/commitdiff
os: avoid allocation in File.WriteString
authorJosh Bleecher Snyder <josharian@gmail.com>
Sat, 7 Nov 2020 01:37:03 +0000 (17:37 -0800)
committerJosh Bleecher Snyder <josharian@gmail.com>
Thu, 25 Feb 2021 19:42:00 +0000 (19:42 +0000)
Instead of alloc+copy to convert the string
to a byte slice, do an unsafe conversion.

Rely on the kernel not to scribble on the
buffer during the write.

Fixes #42406

Change-Id: I66f4838b43a11bcc3d67bbfa1706726318d55343
Reviewed-on: https://go-review.googlesource.com/c/go/+/268020
Trust: Josh Bleecher Snyder <josharian@gmail.com>
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
Reviewed-by: Emmanuel Odeke <emmanuel@orijtech.com>
src/os/file.go
src/os/os_test.go

index 52dd94339b8ffbed5ceaffaec3edd002c19ca36d..ebeb0d0ac9c625fc444d55d0e5df8af7df4b9643 100644 (file)
@@ -44,11 +44,13 @@ import (
        "errors"
        "internal/poll"
        "internal/testlog"
+       "internal/unsafeheader"
        "io"
        "io/fs"
        "runtime"
        "syscall"
        "time"
+       "unsafe"
 )
 
 // Name returns the name of the file as presented to Open.
@@ -246,7 +248,12 @@ func (f *File) Seek(offset int64, whence int) (ret int64, err error) {
 // WriteString is like Write, but writes the contents of string s rather than
 // a slice of bytes.
 func (f *File) WriteString(s string) (n int, err error) {
-       return f.Write([]byte(s))
+       var b []byte
+       hdr := (*unsafeheader.Slice)(unsafe.Pointer(&b))
+       hdr.Data = (*unsafeheader.String)(unsafe.Pointer(&s)).Data
+       hdr.Cap = len(s)
+       hdr.Len = len(s)
+       return f.Write(b)
 }
 
 // Mkdir creates a new directory with the specified name and permission
index a32e5fc11edfa4c5e47fc33c982b731522cad0c8..f27c796c0574ee94a999509ea5c54051f6f953cc 100644 (file)
@@ -2773,3 +2773,21 @@ func TestReadFileProc(t *testing.T) {
                t.Fatalf("read %s: not newline-terminated: %q", name, data)
        }
 }
+
+func TestWriteStringAlloc(t *testing.T) {
+       if runtime.GOOS == "js" {
+               t.Skip("js allocates a lot during File.WriteString")
+       }
+       d := t.TempDir()
+       f, err := Create(filepath.Join(d, "whiteboard.txt"))
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer f.Close()
+       allocs := testing.AllocsPerRun(100, func() {
+               f.WriteString("I will not allocate when passed a string longer than 32 bytes.\n")
+       })
+       if allocs != 0 {
+               t.Errorf("expected 0 allocs for File.WriteString, got %v", allocs)
+       }
+}