-
Notifications
You must be signed in to change notification settings - Fork 36
/
zerocopy.go
135 lines (119 loc) · 3.27 KB
/
zerocopy.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
// Copyright (c) 2019 Andrei Tudor Călin <mail@acln.ro>
// All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package zerocopy
import (
"io"
"os"
"syscall"
)
// A Pipe is a buffered, unidirectional data channel.
type Pipe struct {
r, w *os.File
rrc, wrc syscall.RawConn
teerd io.Reader
teepipe *Pipe
}
// NewPipe creates a new pipe.
func NewPipe() (*Pipe, error) {
r, w, err := os.Pipe()
if err != nil {
return nil, err
}
rrc, err := r.SyscallConn()
if err != nil {
return nil, err
}
wrc, err := w.SyscallConn()
if err != nil {
return nil, err
}
return &Pipe{
r: r,
w: w,
rrc: rrc,
wrc: wrc,
teerd: r,
}, nil
}
// BufferSize returns the buffer size of the pipe.
func (p *Pipe) BufferSize() (int, error) {
return p.bufferSize()
}
// SetBufferSize sets the pipe's buffer size to n.
func (p *Pipe) SetBufferSize(n int) error {
return p.setBufferSize(n)
}
// Read reads data from the pipe.
func (p *Pipe) Read(b []byte) (n int, err error) {
return p.read(b)
}
// CloseRead closes the read side of the pipe.
func (p *Pipe) CloseRead() error {
return p.r.Close()
}
// Write writes data to the pipe.
func (p *Pipe) Write(b []byte) (n int, err error) {
return p.w.Write(b)
}
// CloseWrite closes the write side of the pipe.
func (p *Pipe) CloseWrite() error {
return p.w.Close()
}
// Close closes both sides of the pipe.
func (p *Pipe) Close() error {
err := p.r.Close()
err1 := p.w.Close()
if err != nil {
return err
}
return err1
}
// ReadFrom transfers data from src to the pipe.
//
// If src implements syscall.Conn, ReadFrom tries to use splice(2) for the
// data transfer from the source file descriptor to the pipe. If that is
// not possible, ReadFrom falls back to a generic copy.
func (p *Pipe) ReadFrom(src io.Reader) (int64, error) {
return p.readFrom(src)
}
// WriteTo transfers data from the pipe to dst.
//
// If dst implements syscall.Conn, WriteTo tries to use splice(2) for the
// data transfer from the pipe to the destination file descriptor. If that
// is not possible, WriteTo falls back to a generic copy.
func (p *Pipe) WriteTo(dst io.Writer) (int64, error) {
return p.writeTo(dst)
}
// Tee arranges for data in the read side of the pipe to be mirrored to the
// specified writer. There is no internal buffering: writes must complete
// before the associated read completes.
//
// If the argument is of concrete type *Pipe, the tee(2) system call
// is used when mirroring data from the read side of the pipe.
//
// Tee must not be called concurrently with I/O methods, and must be called
// only once, and before any calls to Read or WriteTo.
func (p *Pipe) Tee(w io.Writer) {
p.tee(w)
}
// Transfer is like io.Copy, but moves data through a pipe rather than through
// a userspace buffer. Given a pipe p, Transfer operates equivalently to
// p.ReadFrom(src) and p.WriteTo(dst), but in lock-step, and with no need
// to create additional goroutines.
//
// Conceptually:
//
// Transfer(upstream, downstream)
//
// is equivalent to
//
// p, _ := NewPipe()
// go p.ReadFrom(downstream)
// p.WriteTo(upstream)
//
// but in more compact form, and slightly more resource-efficient.
func Transfer(dst io.Writer, src io.Reader) (int64, error) {
return transfer(dst, src)
}