github.com/haraldrudell/parl@v0.4.176/pio/closer-buffer.go (about) 1 /* 2 © 2021–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/) 3 ISC License 4 */ 5 6 package pio 7 8 import ( 9 "bytes" 10 "io" 11 "io/fs" 12 "sync/atomic" 13 14 "github.com/haraldrudell/parl/perrors" 15 ) 16 17 // CloserBuffer extends byte.Buffer to be [io.Closer] 18 type CloserBuffer struct { 19 // Available() AvailableBuffer() Bytes() Cap() Grow() Len() Next() 20 // Read() ReadByte() ReadBytes() ReadFrom() ReadRune() ReadString() 21 // Reset() String() Truncate() UnreadByte() UnreadRune() 22 // Write() WriteByte() WriteRune() WriteString() WriteTo() 23 bytes.Buffer 24 isClosed atomic.Bool 25 } 26 27 var _ io.Closer = &CloserBuffer{} // Close() 28 29 var _ io.Reader = &CloserBuffer{} // Read() 30 var _ io.ReaderFrom = &CloserBuffer{} // ReadFrom() 31 var _ io.RuneReader = &CloserBuffer{} // ReadRune() 32 var _ io.ByteReader = &CloserBuffer{} // ReadByte() 33 34 var _ io.Writer = &CloserBuffer{} // Write() 35 var _ io.WriterTo = &CloserBuffer{} // WriteTo() 36 var _ io.StringWriter = &CloserBuffer{} // WriteString() 37 var _ io.ByteWriter = &CloserBuffer{} // WriteByte() 38 39 var _ io.ByteScanner = &CloserBuffer{} // UnreadByte() 40 var _ io.RuneScanner = &CloserBuffer{} // UnreadRune() 41 42 // interfaces that do not exist 43 //var _ io.StringReader = &CloserBuffer{} 44 //var _ io.RuneWriter = &CloserBuffer{} 45 //var _ io.StringScanner = &CloserBuffer{} 46 47 // NewCloserBuffer returns an [bytes.Buffer] implementing [io.Closer] 48 // - if buffer is present, it is copied 49 // - implements: 50 // - [io.Closer] [io.ReadCloser] [io.WriteCloser] [io.ReadWriteCloser] 51 // - [io.ReadWriter] 52 // - [io.Reader] [io.WriterTo] 53 // - [io.Writer] [io.ReaderFrom] 54 // - [io.ByteReader] [io.RuneReader] 55 // - [io.StringWriter] [io.ByteWriter] 56 // - [io.ByteScanner] [io.RuneScanner] 57 func NewCloserBuffer(buffer ...*bytes.Buffer) (closer *CloserBuffer) { 58 c := CloserBuffer{} 59 if len(buffer) > 0 { 60 if b := buffer[0]; b != nil { 61 c.Buffer = *b 62 } 63 } 64 return &c 65 } 66 67 // Read reads up to len(p) bytes into p. It returns the number of bytes 68 // read (0 <= n <= len(p)) and any error encountered. 69 func (b *CloserBuffer) Read(p []byte) (n int, err error) { 70 if err = b.readCheck(); err != nil { 71 return 72 } 73 return b.Buffer.Read(p) 74 } 75 76 // ReadByte reads and returns the next byte from the input or 77 // any error encountered. If ReadByte returns an error, no input 78 // byte was consumed, and the returned byte value is undefined. 79 func (b *CloserBuffer) ReadByte() (c byte, err error) { 80 if err = b.readCheck(); err != nil { 81 return 82 } 83 return b.Buffer.ReadByte() 84 } 85 86 // ReadFrom reads data from r until EOF or error. 87 // The return value n is the number of bytes read. 88 // Any error except EOF encountered during the read is also returned. 89 func (b *CloserBuffer) ReadFrom(r io.Reader) (n int64, err error) { 90 if err = b.closeCheck(); err != nil { 91 return 92 } 93 return b.Buffer.ReadFrom(r) 94 } 95 96 // ReadRune reads a single encoded Unicode character 97 // and returns the rune and its size in bytes. If no character is 98 // available, err will be set. 99 func (b *CloserBuffer) ReadRune() (r rune, size int, err error) { 100 if err = b.readCheck(); err != nil { 101 return 102 } 103 return b.Buffer.ReadRune() 104 } 105 106 // UnreadByte causes the next call to ReadByte to return the last byte read. 107 func (b *CloserBuffer) UnreadByte() (err error) { 108 if err = b.readCheck(); err != nil { 109 return 110 } 111 return b.Buffer.UnreadByte() 112 } 113 114 // UnreadRune causes the next call to ReadRune to return the last rune read. 115 func (b *CloserBuffer) UnreadRune() (err error) { 116 if err = b.readCheck(); err != nil { 117 return 118 } 119 return b.Buffer.UnreadRune() 120 } 121 122 // Write writes len(p) bytes from p to the underlying data stream. 123 // It returns the number of bytes written from p (0 <= n <= len(p)) 124 // and any error encountered that caused the write to stop early. 125 // Write must return a non-nil error if it returns n < len(p). 126 func (b *CloserBuffer) Write(p []byte) (n int, err error) { 127 if err = b.closeCheck(); err != nil { 128 return 129 } 130 return b.Buffer.Write(p) 131 } 132 133 // WriteByte writes a byte to w. 134 func (b *CloserBuffer) WriteByte(c byte) (err error) { 135 if err = b.closeCheck(); err != nil { 136 return 137 } 138 return b.Buffer.WriteByte(c) 139 } 140 141 // WriteString writes the contents of the string s to w, which accepts a slice of bytes. 142 func (b *CloserBuffer) WriteString(s string) (n int, err error) { 143 if err = b.closeCheck(); err != nil { 144 return 145 } 146 return b.Buffer.WriteString(s) 147 } 148 149 // WriteTo writes data to w until there's no more data to write or 150 // when an error occurs. The return value n is the number of bytes 151 // written. Any error encountered during the write is also returned. 152 func (b *CloserBuffer) WriteTo(w io.Writer) (n int64, err error) { 153 if err = b.readCheck(); err != nil { 154 return 155 } 156 return b.Buffer.WriteTo(w) 157 } 158 159 // Reset resets the buffer to be empty, 160 // but it retains the underlying storage for use by future writes. 161 func (b *CloserBuffer) Reset() { 162 b.isClosed.Store(false) 163 b.Buffer.Reset() 164 } 165 166 // Close should only be invoked once. 167 // Close is not required for releasing resources. 168 func (b *CloserBuffer) Close() (err error) { 169 if b.isClosed.Load() || !b.isClosed.CompareAndSwap(false, true) { 170 err = perrors.ErrorfPF("%w", fs.ErrClosed) 171 return // already closed or other thread closed 172 } 173 // noop: there isn’t actually a close 174 return 175 } 176 177 // readCheck check for close for any read method 178 // - a closed stream has deferred close allowing to read until the end 179 func (b *CloserBuffer) readCheck() (err error) { 180 if !b.isClosed.Load() || b.Buffer.Len() > 0 { 181 return 182 } 183 err = perrors.ErrorfPF("%w", fs.ErrClosed) 184 return 185 } 186 187 // closeCheck checks for close for writing methods 188 func (b *CloserBuffer) closeCheck() (err error) { 189 if b.isClosed.Load() || !b.isClosed.CompareAndSwap(false, true) { 190 err = perrors.ErrorfPF("%w", fs.ErrClosed) 191 } 192 return 193 }