|
| 1 | +package io |
| 2 | + |
| 3 | +import ( |
| 4 | + "io" |
| 5 | + "strings" |
| 6 | + "unicode/utf8" |
| 7 | +) |
| 8 | + |
| 9 | +//UnbufferedRuneReader doesn't attempt to buffer the underlying reader, but does buffer enough to decode utf8 runes. |
| 10 | +type UnbufferedRuneReader struct { |
| 11 | + reader io.Reader |
| 12 | + buf [utf8.UTFMax]byte // used only inside ReadRune |
| 13 | + single [1]byte // to read one byte |
| 14 | +} |
| 15 | + |
| 16 | +//ReadLine reads a line from any reader. |
| 17 | +func ReadLine(reader io.Reader) (string, error) { |
| 18 | + rr := ToRuneReader(reader) |
| 19 | + var sb strings.Builder |
| 20 | + var r rune |
| 21 | + var err error |
| 22 | + for r, _, err = rr.ReadRune(); err == nil && r != '\n'; r, _, err = rr.ReadRune() { |
| 23 | + sb.WriteRune(r) |
| 24 | + } |
| 25 | + if err == io.EOF && sb.Len() > 0 { |
| 26 | + err = nil |
| 27 | + } |
| 28 | + return sb.String(), err |
| 29 | +} |
| 30 | + |
| 31 | +//ToRuneReader Converts reader to an io.RuneReader |
| 32 | +func ToRuneReader(reader io.Reader) io.RuneReader { |
| 33 | + if ret, ok := reader.(io.RuneReader); ok { |
| 34 | + return ret |
| 35 | + } |
| 36 | + return &UnbufferedRuneReader{ |
| 37 | + reader: reader, |
| 38 | + } |
| 39 | +} |
| 40 | + |
| 41 | +func (u *UnbufferedRuneReader) readByte() (b byte, err error) { |
| 42 | + n, err := io.ReadFull(u.reader, u.single[:]) |
| 43 | + if n != 1 { |
| 44 | + return 0, err |
| 45 | + } |
| 46 | + return u.single[0], err |
| 47 | +} |
| 48 | + |
| 49 | +//ReadRune reads a single rune, and returns the rune, its byte-length, and possibly an error. |
| 50 | +// see the code for fmt.Scanln - which is not public, but which does, but tokenizing on space, which is not desirable. |
| 51 | +// The implementation in fmt.Scanln also implements io.RuneScanner, which is not needed here as newlines are discarded. |
| 52 | +func (u *UnbufferedRuneReader) ReadRune() (r rune, size int, err error) { |
| 53 | + u.buf[0], err = u.readByte() |
| 54 | + if err != nil { |
| 55 | + return |
| 56 | + } |
| 57 | + if u.buf[0] < utf8.RuneSelf { // fast check for common ASCII case |
| 58 | + r = rune(u.buf[0]) |
| 59 | + size = 1 |
| 60 | + return |
| 61 | + } |
| 62 | + var n int |
| 63 | + for n = 1; !utf8.FullRune(u.buf[:n]); n++ { |
| 64 | + u.buf[n], err = u.readByte() |
| 65 | + if err != nil { |
| 66 | + if err == io.EOF { |
| 67 | + err = nil |
| 68 | + break |
| 69 | + } |
| 70 | + return |
| 71 | + } |
| 72 | + } |
| 73 | + r, size = utf8.DecodeRune(u.buf[:n]) |
| 74 | + return |
| 75 | +} |
0 commit comments