1// +build !wasm
2
3package buf
4
5import (
6	"io"
7	"runtime"
8	"syscall"
9
10	"github.com/v2fly/v2ray-core/v4/common/platform"
11)
12
13type allocStrategy struct {
14	current uint32
15}
16
17func (s *allocStrategy) Current() uint32 {
18	return s.current
19}
20
21func (s *allocStrategy) Adjust(n uint32) {
22	if n >= s.current {
23		s.current *= 4
24	} else {
25		s.current = n
26	}
27
28	if s.current > 32 {
29		s.current = 32
30	}
31
32	if s.current == 0 {
33		s.current = 1
34	}
35}
36
37func (s *allocStrategy) Alloc() []*Buffer {
38	bs := make([]*Buffer, s.current)
39	for i := range bs {
40		bs[i] = New()
41	}
42	return bs
43}
44
45type multiReader interface {
46	Init([]*Buffer)
47	Read(fd uintptr) int32
48	Clear()
49}
50
51// ReadVReader is a Reader that uses readv(2) syscall to read data.
52type ReadVReader struct {
53	io.Reader
54	rawConn syscall.RawConn
55	mr      multiReader
56	alloc   allocStrategy
57}
58
59// NewReadVReader creates a new ReadVReader.
60func NewReadVReader(reader io.Reader, rawConn syscall.RawConn) *ReadVReader {
61	return &ReadVReader{
62		Reader:  reader,
63		rawConn: rawConn,
64		alloc: allocStrategy{
65			current: 1,
66		},
67		mr: newMultiReader(),
68	}
69}
70
71func (r *ReadVReader) readMulti() (MultiBuffer, error) {
72	bs := r.alloc.Alloc()
73
74	r.mr.Init(bs)
75	var nBytes int32
76	err := r.rawConn.Read(func(fd uintptr) bool {
77		n := r.mr.Read(fd)
78		if n < 0 {
79			return false
80		}
81
82		nBytes = n
83		return true
84	})
85	r.mr.Clear()
86
87	if err != nil {
88		ReleaseMulti(MultiBuffer(bs))
89		return nil, err
90	}
91
92	if nBytes == 0 {
93		ReleaseMulti(MultiBuffer(bs))
94		return nil, io.EOF
95	}
96
97	nBuf := 0
98	for nBuf < len(bs) {
99		if nBytes <= 0 {
100			break
101		}
102		end := nBytes
103		if end > Size {
104			end = Size
105		}
106		bs[nBuf].end = end
107		nBytes -= end
108		nBuf++
109	}
110
111	for i := nBuf; i < len(bs); i++ {
112		bs[i].Release()
113		bs[i] = nil
114	}
115
116	return MultiBuffer(bs[:nBuf]), nil
117}
118
119// ReadMultiBuffer implements Reader.
120func (r *ReadVReader) ReadMultiBuffer() (MultiBuffer, error) {
121	if r.alloc.Current() == 1 {
122		b, err := ReadBuffer(r.Reader)
123		if b.IsFull() {
124			r.alloc.Adjust(1)
125		}
126		return MultiBuffer{b}, err
127	}
128
129	mb, err := r.readMulti()
130	if err != nil {
131		return nil, err
132	}
133	r.alloc.Adjust(uint32(len(mb)))
134	return mb, nil
135}
136
137var useReadv = false
138
139func init() {
140	const defaultFlagValue = "NOT_DEFINED_AT_ALL"
141	value := platform.NewEnvFlag("v2ray.buf.readv").GetValue(func() string { return defaultFlagValue })
142	switch value {
143	case defaultFlagValue, "auto":
144		if (runtime.GOARCH == "386" || runtime.GOARCH == "amd64" || runtime.GOARCH == "s390x") && (runtime.GOOS == "linux" || runtime.GOOS == "darwin" || runtime.GOOS == "windows") {
145			useReadv = true
146		}
147	case "enable":
148		useReadv = true
149	}
150}
151