You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

101 lines
2.3 KiB

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