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 }