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.

115 line
2.7 KiB

  1. package iconv
  2. import (
  3. "io"
  4. "runtime"
  5. )
  6. const (
  7. defaultReadBufferSize = 8 * 1024
  8. minReadBufferSize = 16
  9. )
  10. type Reader struct {
  11. source io.Reader
  12. converter *Converter
  13. buffer []byte
  14. readPos, writePos int
  15. eof bool
  16. }
  17. func NewReader(source io.Reader, fromEncoding, toEncoding string) (*Reader, error) {
  18. return NewReaderSized(source, fromEncoding, toEncoding, defaultReadBufferSize)
  19. }
  20. func NewReaderFromConverter(source io.Reader, converter *Converter) *Reader {
  21. return NewReaderFromConverterSized(source, converter, defaultReadBufferSize)
  22. }
  23. func NewReaderSized(source io.Reader, fromEncoding, toEncoding string, size int) (*Reader, error) {
  24. converter, err := NewConverter(fromEncoding, toEncoding)
  25. if err != nil {
  26. return nil, err
  27. }
  28. // add a finalizer for the converter we created
  29. runtime.SetFinalizer(converter, finalizeConverter)
  30. return NewReaderFromConverterSized(source, converter, size), nil
  31. }
  32. func NewReaderFromConverterSized(source io.Reader, converter *Converter, size int) *Reader {
  33. if size < minReadBufferSize {
  34. size = minReadBufferSize
  35. }
  36. return &Reader{
  37. source: source,
  38. converter: converter,
  39. buffer: make([]byte, size),
  40. }
  41. }
  42. func (r *Reader) Read(p []byte) (int, error) {
  43. if len(p) == 0 {
  44. return 0, nil
  45. }
  46. var bytesRead, bytesWritten int
  47. var err error
  48. // setup for a single read into buffer if possible
  49. if !r.eof {
  50. if r.readPos > 0 {
  51. // slide data to front of buffer
  52. r.readPos, r.writePos = 0, copy(r.buffer, r.buffer[r.readPos:r.writePos])
  53. }
  54. if r.writePos < len(r.buffer) {
  55. // do the single read
  56. bytesRead, err = r.source.Read(r.buffer[r.writePos:])
  57. if bytesRead < 0 {
  58. panic("iconv: source reader returned negative count from Read")
  59. }
  60. r.writePos += bytesRead
  61. r.eof = err == io.EOF
  62. }
  63. }
  64. if r.readPos < r.writePos || r.eof {
  65. // convert any buffered data we have, or do a final reset (for shift based conversions)
  66. bytesRead, bytesWritten, err = r.converter.Convert(r.buffer[r.readPos:r.writePos], p)
  67. r.readPos += bytesRead
  68. // if we experienced an iconv error and didn't make progress, report it.
  69. // if we did make progress, it may be informational only (i.e. reporting
  70. // an EILSEQ even when using //ignore to skip them)
  71. if err != nil && bytesWritten == 0 {
  72. return bytesWritten, err
  73. }
  74. // signal an EOF only if we didn't write anything - accomodates premature
  75. // errror checking in user code
  76. if bytesWritten == 0 && r.eof {
  77. return 0, io.EOF
  78. }
  79. return bytesWritten, nil
  80. }
  81. return 0, err
  82. }
  83. func (r *Reader) Reset(source io.Reader) {
  84. r.converter.Reset()
  85. *r = Reader{
  86. source: source,
  87. converter: r.converter,
  88. buffer: r.buffer,
  89. }
  90. }