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
 | 
						|
}
 |