| // Package buffruneio is a wrapper around bufio to provide buffered runes access with unlimited unreads. |
| package buffruneio |
| |
| import ( |
| "bufio" |
| "container/list" |
| "errors" |
| "io" |
| ) |
| |
| // Rune to indicate end of file. |
| const ( |
| EOF = -(iota + 1) |
| ) |
| |
| // ErrNoRuneToUnread is returned by UnreadRune() when the read index is already at the beginning of the buffer. |
| var ErrNoRuneToUnread = errors.New("no rune to unwind") |
| |
| // Reader implements runes buffering for an io.Reader object. |
| type Reader struct { |
| buffer *list.List |
| current *list.Element |
| input *bufio.Reader |
| } |
| |
| // NewReader returns a new Reader. |
| func NewReader(rd io.Reader) *Reader { |
| return &Reader{ |
| buffer: list.New(), |
| input: bufio.NewReader(rd), |
| } |
| } |
| |
| func (rd *Reader) feedBuffer() error { |
| r, _, err := rd.input.ReadRune() |
| |
| if err != nil { |
| if err != io.EOF { |
| return err |
| } |
| r = EOF |
| } |
| |
| rd.buffer.PushBack(r) |
| if rd.current == nil { |
| rd.current = rd.buffer.Back() |
| } |
| return nil |
| } |
| |
| // ReadRune reads the next rune from buffer, or from the underlying reader if needed. |
| func (rd *Reader) ReadRune() (rune, error) { |
| if rd.current == rd.buffer.Back() || rd.current == nil { |
| err := rd.feedBuffer() |
| if err != nil { |
| return EOF, err |
| } |
| } |
| |
| r := rd.current.Value |
| rd.current = rd.current.Next() |
| return r.(rune), nil |
| } |
| |
| // UnreadRune pushes back the previously read rune in the buffer, extending it if needed. |
| func (rd *Reader) UnreadRune() error { |
| if rd.current == rd.buffer.Front() { |
| return ErrNoRuneToUnread |
| } |
| if rd.current == nil { |
| rd.current = rd.buffer.Back() |
| } else { |
| rd.current = rd.current.Prev() |
| } |
| return nil |
| } |
| |
| // Forget removes runes stored before the current stream position index. |
| func (rd *Reader) Forget() { |
| if rd.current == nil { |
| rd.current = rd.buffer.Back() |
| } |
| for ; rd.current != rd.buffer.Front(); rd.buffer.Remove(rd.current.Prev()) { |
| } |
| } |
| |
| // Peek returns at most the next n runes, reading from the uderlying source if |
| // needed. Does not move the current index. It includes EOF if reached. |
| func (rd *Reader) Peek(n int) []rune { |
| res := make([]rune, 0, n) |
| cursor := rd.current |
| for i := 0; i < n; i++ { |
| if cursor == nil { |
| err := rd.feedBuffer() |
| if err != nil { |
| return res |
| } |
| cursor = rd.buffer.Back() |
| } |
| if cursor != nil { |
| r := cursor.Value.(rune) |
| res = append(res, r) |
| if r == EOF { |
| return res |
| } |
| cursor = cursor.Next() |
| } |
| } |
| return res |
| } |