github.com/codingeasygo/util@v0.0.0-20231206062002-1ce2f004b7d9/xio/pipe.go (about) 1 package xio 2 3 import ( 4 "fmt" 5 "net" 6 "sync" 7 "time" 8 ) 9 10 // PipedChan provoider Write buffer to Read implement by chan 11 type PipedChan struct { 12 closed uint32 13 having []byte 14 locker *sync.Cond 15 } 16 17 // NewPipedChan will return new PipedChan 18 func NewPipedChan() (piped *PipedChan) { 19 piped = &PipedChan{ 20 locker: sync.NewCond(&sync.Mutex{}), 21 } 22 return 23 } 24 25 func (p *PipedChan) Read(b []byte) (n int, err error) { 26 p.locker.L.Lock() 27 defer p.locker.L.Unlock() 28 if len(p.having) < 1 { 29 if p.closed > 0 { 30 err = fmt.Errorf("closed") 31 return 32 } 33 p.locker.Wait() 34 if len(p.having) < 1 { 35 err = fmt.Errorf("closed") 36 return 37 } 38 } 39 n = copy(b, p.having) 40 p.having = p.having[n:] 41 if len(p.having) < 1 { 42 p.locker.Signal() 43 } 44 return 45 } 46 47 func (p *PipedChan) Write(b []byte) (n int, err error) { 48 p.locker.L.Lock() 49 defer p.locker.L.Unlock() 50 if p.closed < 1 && len(p.having) > 0 { 51 p.locker.Wait() 52 } 53 if p.closed > 0 { 54 err = fmt.Errorf("closed") 55 return 56 } 57 if len(p.having) > 0 { 58 panic("having") 59 } 60 p.having = make([]byte, len(b)) 61 n = copy(p.having, b) 62 p.locker.Signal() 63 return 64 } 65 66 // Close will close piped channel 67 func (p *PipedChan) Close() (err error) { 68 p.locker.L.Lock() 69 defer p.locker.L.Unlock() 70 if p.closed > 0 { 71 err = fmt.Errorf("closed") 72 return 73 } 74 p.closed = 1 75 p.locker.Broadcast() 76 return 77 } 78 79 // PipeReadWriteCloser is pipe connection 80 type PipeReadWriteCloser struct { 81 Alias string 82 reader *PipedChan 83 writer *PipedChan 84 side *PipeReadWriteCloser 85 } 86 87 // Pipe will return new pipe connection. 88 func Pipe() (a, b *PipeReadWriteCloser, err error) { 89 piperA := NewPipedChan() 90 piperB := NewPipedChan() 91 92 a = &PipeReadWriteCloser{ 93 reader: piperA, 94 writer: piperB, 95 Alias: "piped", 96 } 97 b = &PipeReadWriteCloser{ 98 reader: piperB, 99 writer: piperA, 100 Alias: "piped", 101 } 102 a.side = b 103 b.side = a 104 return 105 } 106 107 func (p *PipeReadWriteCloser) Read(b []byte) (n int, err error) { 108 n, err = p.reader.Read(b) 109 return 110 } 111 112 func (p *PipeReadWriteCloser) Write(b []byte) (n int, err error) { 113 n, err = p.writer.Write(b) 114 return 115 } 116 117 // Close will close reader/writer 118 func (p *PipeReadWriteCloser) Close() (err error) { 119 p.reader.Close() 120 p.writer.Close() 121 p.side.reader.Close() 122 p.side.writer.Close() 123 return 124 } 125 126 func (p *PipeReadWriteCloser) String() string { 127 return p.Alias 128 } 129 130 // PipedConn is an implementation of the net.Conn interface for piped two connection. 131 type PipedConn struct { 132 *PipeReadWriteCloser 133 } 134 135 // CreatePipedConn will return two piped connection. 136 func CreatePipedConn() (a, b *PipedConn, err error) { 137 basea, baseb, err := Pipe() 138 if err == nil { 139 a = &PipedConn{PipeReadWriteCloser: basea} 140 b = &PipedConn{PipeReadWriteCloser: baseb} 141 } 142 return 143 } 144 145 // LocalAddr return self 146 func (p *PipedConn) LocalAddr() net.Addr { 147 return p 148 } 149 150 // RemoteAddr return self 151 func (p *PipedConn) RemoteAddr() net.Addr { 152 return p 153 } 154 155 // SetDeadline is empty 156 func (p *PipedConn) SetDeadline(t time.Time) error { 157 return nil 158 } 159 160 // SetReadDeadline is empty 161 func (p *PipedConn) SetReadDeadline(t time.Time) error { 162 return nil 163 } 164 165 // SetWriteDeadline is empty 166 func (p *PipedConn) SetWriteDeadline(t time.Time) error { 167 return nil 168 } 169 170 // Network return "piped" 171 func (p *PipedConn) Network() string { 172 return "piped" 173 } 174 175 func (p *PipedConn) String() string { 176 return p.PipeReadWriteCloser.String() 177 } 178 179 type PipedListener struct { 180 queue chan *PipedConn 181 } 182 183 func NewPipedListener() (listener *PipedListener) { 184 listener = &PipedListener{ 185 queue: make(chan *PipedConn, 8), 186 } 187 return 188 } 189 190 func (p *PipedListener) Dial() (conn net.Conn, err error) { 191 conna, connb, err := CreatePipedConn() 192 if err == nil { 193 p.queue <- conna 194 conn = connb 195 } 196 return 197 } 198 199 func (p *PipedListener) Accept() (conn net.Conn, err error) { 200 c := <-p.queue 201 if c == nil { 202 err = fmt.Errorf("closed") 203 } else { 204 conn = c 205 } 206 return 207 } 208 209 func (p *PipedListener) Close() (err error) { 210 p.queue <- nil 211 return 212 } 213 214 func (p *PipedListener) Addr() net.Addr { 215 return p 216 } 217 218 // Network return "piped" 219 func (p *PipedListener) Network() string { 220 return "piped" 221 } 222 223 func (p *PipedListener) String() string { 224 return fmt.Sprintf("%p", p) 225 }