forked from Monibuca/engine
-
Notifications
You must be signed in to change notification settings - Fork 0
/
vecio.go
205 lines (176 loc) · 4.91 KB
/
vecio.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
package util
import (
//"fmt"
"io"
"net"
"os"
"unsafe"
)
/*
#include <sys/uio.h>
// Structure for scatter/gather I/O.
struct iovec{
void *iov_base; // Pointer to data.
size_t iov_len; // Length of data.
};
*/
type SysIOVec struct {
Base uintptr
Length uint64
}
type IOVec struct {
Data [][]byte
Length int
index int
}
func (iov *IOVec) Append(b []byte) {
iov.Data = append(iov.Data, b)
iov.Length += len(b)
}
// Data模型:
// index -> | Data[0][0] | Data[0][1] | Data[0][2] | ... | Data[0][n] |
// | Data[1][0] | Data[1][1] | Data[1][2] | ... | Data[1][n] |
// ......
// | Data[n][0] | Data[n][1] | Data[n][2] | ... | Data[n][n] |
//
// index是下标
func (iov *IOVec) WriteTo(w io.Writer, n int) (written int, err error) {
for n > 0 && iov.Length > 0 {
data := iov.Data[iov.index]
// 用来存放每次需要写入的数据
var b []byte
// 只会读n个字节,超出n个字节,不管
// 如果iov.Data里面有1000个数据,可是每次只读184个字节,那么剩下的数据(856)重新放回Data
if n > len(data) {
b = data
} else {
b = data[:n]
}
// n个字节后面的数据
// 如果这时候n个字节后面已经没有数据了,我们就将下标index往后移一位
// 否则我们将n个字节后面的数据重新放回Data里.
data = data[len(b):]
if len(data) == 0 {
iov.index++
} else {
iov.Data[iov.index] = data
}
n -= len(b)
iov.Length -= len(b)
written += len(b)
if _, err = w.Write(b); err != nil {
return
}
}
return
}
type IOVecWriter struct {
fd uintptr
smallBuffer []byte
sysIOV []SysIOVec
}
func NewIOVecWriter(w io.Writer) (iow *IOVecWriter) {
var err error
var file *os.File
// TODO:是否要增加其他的类型断言
switch value := w.(type) {
case *net.TCPConn:
{
file, err = value.File()
if err != nil {
return
}
}
case *os.File:
{
file = value
}
default:
return
}
iow = &IOVecWriter{
fd: file.Fd(),
}
return
}
// 1 2 3 4 5 6
// --- -------------- --- --- --- -----------
// | | | | | | | | | | | | ......
// --- -------------- --- --- --- -----------
//
// 1 -> 5个字节, 3 -> 15个字节, 4 -> 10个字节, 5 -> 15个字节
// 1,3,4,5内存块太小(小于16个字节),因此我们将它组装起来为samllbuffer
// 并且将Base置于每次组装smallBuffer前总长度的尾部.
//
// samllbuffer:
// 1 3 4 5 ........
// ------------------------------
// | |
// ------------------------------
// <--> 第一个小内存块,假设地址为0xF10000
// 5
// <------> 第二个小内存块,假设地址为0xF20000
// 20
// <----------> 第三个小内存块,假设地址为0xF30000
// 30
// <--------------> 第四个小内存块,假设地址为0xF40000
// 45
//
// 开始Base == 每次组装smallBuffer尾部
// 即:
// Base1 = 0, smallBuffer += 5,
// Base3 = 5, smallBuffer += 15,
// Base4 = 20, smallBuffer += 10,
// Base5 = 30, smallBuffer += 15,
//
// 然后我们将每一块内存块都取出来,比samllBuffer小的内存块,我们就将Base指向内存块的地址
// 之前小于16个字节的内存块,肯定会比smallBuffer小,因为smallBuffer是所有小内存快的总和.
// 即:
// Base1 = &smallBuffer[0], Base1 = 0xF10000,
// Base3 = &smallBuffer[5], Base3 = 0xF20000,
// Base4 = &smallBuffer[20], Base4 = 0xF30000,
// Base5 = &smallBuffer[30], Base5 = 0xF40000,
func (iow *IOVecWriter) Write(data []byte) (written int, err error) {
siov := SysIOVec{
Length: uint64(len(data)),
}
// unsafe.Pointer == void *
// Base 用整数的形式来记录内存中有几个数据
// 如果数据小于16,这个时候小块内存的Base还不是数据的内存地址
if siov.Length < 16 {
// Base 置于上一块samllBuffer的末尾
// 然后拼接smallBuffer
siov.Base = uintptr(len(iow.smallBuffer))
iow.smallBuffer = append(iow.smallBuffer, data...)
} else {
siov.Base = uintptr(unsafe.Pointer(&data[0]))
}
iow.sysIOV = append(iow.sysIOV, siov)
return written, nil
}
func (iow *IOVecWriter) Flush() error {
// 取出每一块内存
for i, _ := range iow.sysIOV {
siov := &iow.sysIOV[i] // 一定要拿地址,如果这里不是取地址,那么无法改变下面Base的值
if siov.Base < uintptr(len(iow.smallBuffer)) {
// 这个时候小块内存的Base就是数据的内存地址
siov.Base = uintptr(unsafe.Pointer(&iow.smallBuffer[siov.Base]))
}
}
N := 1024
count := len(iow.sysIOV)
// 每次最多取1024个内存块(不管是大内存块,还是小内存块)
for i := 0; i < count; i += N {
n := count - i
if n > N {
n = N
}
// _, _, errno := syscall.Syscall(syscall.SYS_WRITEV, iow.fd, uintptr(unsafe.Pointer(&iow.sysIOV[i])), uintptr(n))
// if errno != 0 {
// return errors.New(errno.Error())
// }
}
iow.sysIOV = iow.sysIOV[:0]
iow.smallBuffer = iow.smallBuffer[:0]
return nil
}