diff --git a/common/buf/readv_posix.go b/common/buf/readv_posix.go new file mode 100644 index 00000000..22f6d3ab --- /dev/null +++ b/common/buf/readv_posix.go @@ -0,0 +1,45 @@ +// +build !windows + +package buf + +import ( + "syscall" + "unsafe" +) + +type posixReader struct { + iovecs []syscall.Iovec +} + +func (r *posixReader) Init(bs []*Buffer) { + iovecs := r.iovecs + if iovecs == nil { + iovecs = make([]syscall.Iovec, 0, len(bs)) + } + for idx, b := range bs { + iovecs = append(iovecs, syscall.Iovec{ + Base: &(b.v[0]), + }) + iovecs[idx].SetLen(int(Size)) + } + r.iovecs = iovecs +} + +func (r *posixReader) Read(fd uintptr) int32 { + n, _, e := syscall.Syscall(syscall.SYS_READV, fd, uintptr(unsafe.Pointer(&r.iovecs[0])), uintptr(len(r.iovecs))) + if e != 0 { + return -1 + } + return int32(n) +} + +func (r *posixReader) Clear() { + for idx := range r.iovecs { + r.iovecs[idx].Base = nil + } + r.iovecs = r.iovecs[:0] +} + +func newMultiReader() multiReader { + return &posixReader{} +} diff --git a/common/buf/readv_reader.go b/common/buf/readv_reader.go index 327cf97d..1d266e2e 100644 --- a/common/buf/readv_reader.go +++ b/common/buf/readv_reader.go @@ -1,64 +1,84 @@ -// +build !windows - package buf import ( "io" "runtime" "syscall" - "unsafe" "v2ray.com/core/common/platform" ) +type allocStrategy struct { + current uint32 +} + +func (s *allocStrategy) Current() uint32 { + return s.current +} + +func (s *allocStrategy) Adjust(n uint32) { + if n >= s.current { + s.current *= 4 + } else { + s.current = n + } + + if s.current > 32 { + s.current = 32 + } + + if s.current == 0 { + s.current = 1 + } +} + +func (s *allocStrategy) Alloc() []*Buffer { + bs := make([]*Buffer, s.current) + for i := range bs { + bs[i] = New() + } + return bs +} + +type multiReader interface { + Init([]*Buffer) + Read(fd uintptr) int32 + Clear() +} + type ReadVReader struct { io.Reader rawConn syscall.RawConn - iovects []syscall.Iovec - nBuf int32 + mr multiReader + alloc allocStrategy } func NewReadVReader(reader io.Reader, rawConn syscall.RawConn) *ReadVReader { return &ReadVReader{ Reader: reader, rawConn: rawConn, - nBuf: 1, + alloc: allocStrategy{ + current: 1, + }, + mr: newMultiReader(), } } -func allocN(n int32) []*Buffer { - bs := make([]*Buffer, 0, n) - for i := int32(0); i < n; i++ { - bs = append(bs, New()) - } - return bs -} - func (r *ReadVReader) readMulti() (MultiBuffer, error) { - bs := allocN(r.nBuf) - - var iovecs []syscall.Iovec - if r.iovects != nil { - iovecs = r.iovects - } - for idx, b := range bs { - iovecs = append(iovecs, syscall.Iovec{ - Base: &(b.v[0]), - }) - iovecs[idx].SetLen(int(Size)) - } - r.iovects = iovecs[:0] - - var nBytes int + bs := r.alloc.Alloc() + r.mr.Init(bs) + var nBytes int32 err := r.rawConn.Read(func(fd uintptr) bool { - n, _, e := syscall.Syscall(syscall.SYS_READV, fd, uintptr(unsafe.Pointer(&iovecs[0])), uintptr(len(iovecs))) - if e != 0 { + n := r.mr.Read(fd) + if n < 0 { return false } - nBytes = int(n) + + nBytes = n return true }) + r.mr.Clear() if err != nil { mb := MultiBuffer(bs) @@ -82,7 +102,7 @@ func (r *ReadVReader) readMulti() (MultiBuffer, error) { end = Size } bs[nBuf].end = end - nBytes -= int(end) + nBytes -= end nBuf++ } @@ -96,13 +116,13 @@ func (r *ReadVReader) readMulti() (MultiBuffer, error) { // ReadMultiBuffer implements Reader. func (r *ReadVReader) ReadMultiBuffer() (MultiBuffer, error) { - if r.nBuf == 1 { + if r.alloc.Current() == 1 { b, err := readOne(r.Reader) if err != nil { return nil, err } if b.IsFull() { - r.nBuf = 2 + r.alloc.Adjust(1) } return NewMultiBufferValue(b), nil } @@ -111,12 +131,7 @@ func (r *ReadVReader) ReadMultiBuffer() (MultiBuffer, error) { if err != nil { return nil, err } - nBuf := int32(len(mb)) - if nBuf < r.nBuf { - r.nBuf = nBuf - } else if nBuf == r.nBuf && r.nBuf < 16 { - r.nBuf *= 4 - } + r.alloc.Adjust(uint32(len(mb))) return mb, nil } diff --git a/common/buf/readv_reader_windows.go b/common/buf/readv_reader_windows.go deleted file mode 100644 index 743e9a53..00000000 --- a/common/buf/readv_reader_windows.go +++ /dev/null @@ -1,14 +0,0 @@ -// +build windows - -package buf - -import ( - "io" - "syscall" -) - -const useReadv = false - -func NewReadVReader(reader io.Reader, rawConn syscall.RawConn) Reader { - panic("Shoud not happen") -} diff --git a/common/buf/readv_windows.go b/common/buf/readv_windows.go new file mode 100644 index 00000000..04f5047b --- /dev/null +++ b/common/buf/readv_windows.go @@ -0,0 +1,41 @@ +// +build windows + +package buf + +import "syscall" + +type windowsReader struct { + o syscall.Overlapped + bufs []syscall.WSABuf + flags uint32 + qty uint32 +} + +func (r *windowsReader) Init(bs []*Buffer) { + if r.bufs == nil { + r.bufs = make([]syscall.WSABuf, 0, len(bs)) + } + for _, b := range bs { + r.bufs = append(r.bufs, syscall.WSABuf{Len: uint32(b.Len()), Buf: &b.v[0]}) + } +} + +func (r *windowsReader) Clear() { + for idx := range r.bufs { + r.bufs[idx].Buf = nil + } + r.bufs = r.bufs[:0] +} + +func (r *windowsReader) Read(fd uintptr) int32 { + var nBytes uint32 + err := syscall.WSARecv(syscall.Handle(fd), &r.bufs[0], uint32(len(r.bufs)), &nBytes, &r.flags, &r.o, nil) + if err != nil { + return -1 + } + return int32(nBytes) +} + +func newMultiReader() multiReader { + return new(windowsReader) +}