118 lines
		
	
	
		
			2.5 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			118 lines
		
	
	
		
			2.5 KiB
		
	
	
	
		
			Go
		
	
	
	
| // 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),
 | |
| 	}
 | |
| }
 | |
| 
 | |
| type runeWithSize struct {
 | |
| 	r    rune
 | |
| 	size int
 | |
| }
 | |
| 
 | |
| func (rd *Reader) feedBuffer() error {
 | |
| 	r, size, err := rd.input.ReadRune()
 | |
| 
 | |
| 	if err != nil {
 | |
| 		if err != io.EOF {
 | |
| 			return err
 | |
| 		}
 | |
| 		r = EOF
 | |
| 	}
 | |
| 
 | |
| 	newRuneWithSize := runeWithSize{r, size}
 | |
| 
 | |
| 	rd.buffer.PushBack(newRuneWithSize)
 | |
| 	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, int, error) {
 | |
| 	if rd.current == rd.buffer.Back() || rd.current == nil {
 | |
| 		err := rd.feedBuffer()
 | |
| 		if err != nil {
 | |
| 			return EOF, 0, err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	runeWithSize := rd.current.Value.(runeWithSize)
 | |
| 	rd.current = rd.current.Next()
 | |
| 	return runeWithSize.r, runeWithSize.size, 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()) {
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // PeekRune 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) PeekRunes(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.(runeWithSize).r
 | |
| 			res = append(res, r)
 | |
| 			if r == EOF {
 | |
| 				return res
 | |
| 			}
 | |
| 			cursor = cursor.Next()
 | |
| 		}
 | |
| 	}
 | |
| 	return res
 | |
| }
 |