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  }