github.com/isyscore/isc-gobase@v1.5.3-0.20231218061332-cbc7451899e9/iconv/iconv_reader.go (about)

     1  package iconv
     2  
     3  import (
     4  	"io"
     5  	"syscall"
     6  )
     7  
     8  type Reader struct {
     9  	source            io.Reader
    10  	converter         *Converter
    11  	buffer            []byte
    12  	readPos, writePos int
    13  	err               error
    14  }
    15  
    16  func NewReader(source io.Reader, fromEncoding string, toEncoding string) (*Reader, error) {
    17  	// create a converter
    18  	converter, err := NewConverter(fromEncoding, toEncoding)
    19  	if err == nil {
    20  		return NewReaderFromConverter(source, converter), err
    21  	}
    22  	// return the error
    23  	return nil, err
    24  }
    25  
    26  func NewReaderFromConverter(source io.Reader, converter *Converter) (reader *Reader) {
    27  	reader = new(Reader)
    28  	// copy elements
    29  	reader.source = source
    30  	reader.converter = converter
    31  	// create 8K buffers
    32  	reader.buffer = make([]byte, 8*1024)
    33  	return reader
    34  }
    35  
    36  func (c *Reader) fillBuffer() {
    37  	// slide existing data to beginning
    38  	if c.readPos > 0 {
    39  		// copy current bytes - is this guaranteed safe?
    40  		copy(c.buffer, c.buffer[c.readPos:c.writePos])
    41  		// adjust positions
    42  		c.writePos -= c.readPos
    43  		c.readPos = 0
    44  	}
    45  	// read new data into buffer at write position
    46  	bytesRead, err := c.source.Read(c.buffer[c.writePos:])
    47  	// adjust write position
    48  	c.writePos += bytesRead
    49  	// track any reader error / EOF
    50  	if err != nil {
    51  		c.err = err
    52  	}
    53  }
    54  
    55  // implement the io.Reader interface
    56  func (c *Reader) Read(p []byte) (n int, err error) {
    57  	// checks for when we have no data
    58  	for c.writePos == 0 || c.readPos == c.writePos {
    59  		// if we have an error / EOF, just return it
    60  		if c.err != nil {
    61  			return n, c.err
    62  		}
    63  		// else, fill our buffer
    64  		c.fillBuffer()
    65  	}
    66  	// we should have an appropriate amount of data, convert it into the given buffer
    67  	bytesRead, bytesWritten, err := c.converter.Convert(c.buffer[c.readPos:c.writePos], p)
    68  	// adjust byte counters
    69  	c.readPos += bytesRead
    70  	n += bytesWritten
    71  	// if we experienced an iconv error, check it
    72  	if err != nil {
    73  		// E2BIG errors can be ignored (we'll get them often) as long
    74  		// as at least 1 byte was written. If we experienced an E2BIG
    75  		// and no bytes were written then the buffer is too small for
    76  		// even the next character
    77  		if err != syscall.E2BIG || bytesWritten == 0 {
    78  			// track anything else
    79  			c.err = err
    80  		}
    81  	}
    82  	// return our results
    83  	return n, c.err
    84  }