diff options
Diffstat (limited to 'modules/mahonia/writer.go')
-rw-r--r-- | modules/mahonia/writer.go | 108 |
1 files changed, 108 insertions, 0 deletions
diff --git a/modules/mahonia/writer.go b/modules/mahonia/writer.go new file mode 100644 index 00000000..bdeb519c --- /dev/null +++ b/modules/mahonia/writer.go @@ -0,0 +1,108 @@ +package mahonia + +import ( + "io" + "unicode/utf8" +) + +// Writer implements character-set encoding for an io.Writer object. +type Writer struct { + wr io.Writer + encode Encoder + inbuf []byte + outbuf []byte +} + +// NewWriter creates a new Writer that uses the receiver to encode text. +func (e Encoder) NewWriter(wr io.Writer) *Writer { + w := new(Writer) + w.wr = wr + w.encode = e + return w +} + +// Write encodes and writes the data from p. +func (w *Writer) Write(p []byte) (n int, err error) { + n = len(p) + + if len(w.inbuf) > 0 { + w.inbuf = append(w.inbuf, p...) + p = w.inbuf + } + + if len(w.outbuf) < len(p) { + w.outbuf = make([]byte, len(p)+10) + } + + outpos := 0 + + for len(p) > 0 { + rune, size := utf8.DecodeRune(p) + if rune == 0xfffd && !utf8.FullRune(p) { + break + } + + p = p[size:] + + retry: + size, status := w.encode(w.outbuf[outpos:], rune) + + if status == NO_ROOM { + newDest := make([]byte, len(w.outbuf)*2) + copy(newDest, w.outbuf) + w.outbuf = newDest + goto retry + } + + if status == STATE_ONLY { + outpos += size + goto retry + } + + outpos += size + } + + w.inbuf = w.inbuf[:0] + if len(p) > 0 { + w.inbuf = append(w.inbuf, p...) + } + + n1, err := w.wr.Write(w.outbuf[0:outpos]) + + if err != nil && n1 < n { + n = n1 + } + + return +} + +func (w *Writer) WriteRune(c rune) (size int, err error) { + if len(w.inbuf) > 0 { + // There are leftover bytes, a partial UTF-8 sequence. + w.inbuf = w.inbuf[:0] + w.WriteRune(0xfffd) + } + + if w.outbuf == nil { + w.outbuf = make([]byte, 16) + } + + outpos := 0 + +retry: + size, status := w.encode(w.outbuf[outpos:], c) + + if status == NO_ROOM { + w.outbuf = make([]byte, len(w.outbuf)*2) + goto retry + } + + if status == STATE_ONLY { + outpos += size + goto retry + } + + outpos += size + + return w.wr.Write(w.outbuf[0:outpos]) +} |