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.

108 lines
1.7 KiB

10 years ago
  1. package mahonia
  2. import (
  3. "io"
  4. "unicode/utf8"
  5. )
  6. // Writer implements character-set encoding for an io.Writer object.
  7. type Writer struct {
  8. wr io.Writer
  9. encode Encoder
  10. inbuf []byte
  11. outbuf []byte
  12. }
  13. // NewWriter creates a new Writer that uses the receiver to encode text.
  14. func (e Encoder) NewWriter(wr io.Writer) *Writer {
  15. w := new(Writer)
  16. w.wr = wr
  17. w.encode = e
  18. return w
  19. }
  20. // Write encodes and writes the data from p.
  21. func (w *Writer) Write(p []byte) (n int, err error) {
  22. n = len(p)
  23. if len(w.inbuf) > 0 {
  24. w.inbuf = append(w.inbuf, p...)
  25. p = w.inbuf
  26. }
  27. if len(w.outbuf) < len(p) {
  28. w.outbuf = make([]byte, len(p)+10)
  29. }
  30. outpos := 0
  31. for len(p) > 0 {
  32. rune, size := utf8.DecodeRune(p)
  33. if rune == 0xfffd && !utf8.FullRune(p) {
  34. break
  35. }
  36. p = p[size:]
  37. retry:
  38. size, status := w.encode(w.outbuf[outpos:], rune)
  39. if status == NO_ROOM {
  40. newDest := make([]byte, len(w.outbuf)*2)
  41. copy(newDest, w.outbuf)
  42. w.outbuf = newDest
  43. goto retry
  44. }
  45. if status == STATE_ONLY {
  46. outpos += size
  47. goto retry
  48. }
  49. outpos += size
  50. }
  51. w.inbuf = w.inbuf[:0]
  52. if len(p) > 0 {
  53. w.inbuf = append(w.inbuf, p...)
  54. }
  55. n1, err := w.wr.Write(w.outbuf[0:outpos])
  56. if err != nil && n1 < n {
  57. n = n1
  58. }
  59. return
  60. }
  61. func (w *Writer) WriteRune(c rune) (size int, err error) {
  62. if len(w.inbuf) > 0 {
  63. // There are leftover bytes, a partial UTF-8 sequence.
  64. w.inbuf = w.inbuf[:0]
  65. w.WriteRune(0xfffd)
  66. }
  67. if w.outbuf == nil {
  68. w.outbuf = make([]byte, 16)
  69. }
  70. outpos := 0
  71. retry:
  72. size, status := w.encode(w.outbuf[outpos:], c)
  73. if status == NO_ROOM {
  74. w.outbuf = make([]byte, len(w.outbuf)*2)
  75. goto retry
  76. }
  77. if status == STATE_ONLY {
  78. outpos += size
  79. goto retry
  80. }
  81. outpos += size
  82. return w.wr.Write(w.outbuf[0:outpos])
  83. }