]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - libgo/go/internal/poll/sendfile_windows.go
libgo: update to Go1.14beta1
[thirdparty/gcc.git] / libgo / go / internal / poll / sendfile_windows.go
index 0fe9b9b420922f8acedf5a9dd5c801e675da9071..5674af418952578f25551230ebe3cd42df2f8a45 100644 (file)
@@ -4,10 +4,13 @@
 
 package poll
 
-import "syscall"
+import (
+       "io"
+       "syscall"
+)
 
 // SendFile wraps the TransmitFile call.
-func SendFile(fd *FD, src syscall.Handle, n int64) (int64, error) {
+func SendFile(fd *FD, src syscall.Handle, n int64) (written int64, err error) {
        if fd.kind == kindPipe {
                // TransmitFile does not work with pipes
                return 0, syscall.ESPIPE
@@ -19,26 +22,60 @@ func SendFile(fd *FD, src syscall.Handle, n int64) (int64, error) {
        defer fd.writeUnlock()
 
        o := &fd.wop
-       o.qty = uint32(n)
        o.handle = src
 
        // TODO(brainman): skip calling syscall.Seek if OS allows it
-       curpos, err := syscall.Seek(o.handle, 0, 1)
+       curpos, err := syscall.Seek(o.handle, 0, io.SeekCurrent)
        if err != nil {
                return 0, err
        }
 
-       o.o.Offset = uint32(curpos)
-       o.o.OffsetHigh = uint32(curpos >> 32)
+       if n <= 0 { // We don't know the size of the file so infer it.
+               // Find the number of bytes offset from curpos until the end of the file.
+               n, err = syscall.Seek(o.handle, -curpos, io.SeekEnd)
+               if err != nil {
+                       return
+               }
+               // Now seek back to the original position.
+               if _, err = syscall.Seek(o.handle, curpos, io.SeekStart); err != nil {
+                       return
+               }
+       }
+
+       // TransmitFile can be invoked in one call with at most
+       // 2,147,483,646 bytes: the maximum value for a 32-bit integer minus 1.
+       // See https://docs.microsoft.com/en-us/windows/win32/api/mswsock/nf-mswsock-transmitfile
+       const maxChunkSizePerCall = int64(0x7fffffff - 1)
+
+       for n > 0 {
+               chunkSize := maxChunkSizePerCall
+               if chunkSize > n {
+                       chunkSize = n
+               }
+
+               o.qty = uint32(chunkSize)
+               o.o.Offset = uint32(curpos)
+               o.o.OffsetHigh = uint32(curpos >> 32)
+
+               nw, err := wsrv.ExecIO(o, func(o *operation) error {
+                       return syscall.TransmitFile(o.fd.Sysfd, o.handle, o.qty, 0, &o.o, nil, syscall.TF_WRITE_BEHIND)
+               })
+               if err != nil {
+                       return written, err
+               }
+
+               curpos += int64(nw)
 
-       done, err := wsrv.ExecIO(o, func(o *operation) error {
-               return syscall.TransmitFile(o.fd.Sysfd, o.handle, o.qty, 0, &o.o, nil, syscall.TF_WRITE_BEHIND)
-       })
-       if err == nil {
                // Some versions of Windows (Windows 10 1803) do not set
                // file position after TransmitFile completes.
                // So just use Seek to set file position.
-               _, err = syscall.Seek(o.handle, curpos+int64(done), 0)
+               if _, err = syscall.Seek(o.handle, curpos, io.SeekStart); err != nil {
+                       return written, err
+               }
+
+               n -= int64(nw)
+               written += int64(nw)
        }
-       return int64(done), err
+
+       return
 }