-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbufrr.go
135 lines (117 loc) · 2.71 KB
/
bufrr.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
// Package bufrr provides a buffered rune reader,
// with both PeekRune and UnreadRune.
// It takes an io.Reader providing the source,
// buffers it by wrapping with a bufio.Reader,
// and creates a new Reader implementing
// the bufrr.RunePeeker interface
// (an io.RuneScanner interface plus an
// additional PeekRune method).
//
// Additionally, bufrr.Reader also translates
// io.EOF error into the invalid rune value of -1
// (defined as bufrr.EOF)
//
// Internally, bufrr.Reader is a bufio.Reader
// plus a single-rune peek buffer and a
// single-rune unread buffer.
package bufrr
import (
"bufio"
"errors"
"io"
)
const EOF = -1
var ErrInvalidUnreadRune = errors.New("bufrr: invalid use of UnreadRune")
type Reader struct {
br *bufio.Reader
peek runeCache
last runeCache
}
type RunePeeker interface {
io.RuneScanner
PeekRune() (r rune, w int, err error)
}
func NewReader(rd io.Reader) *Reader {
return &Reader{br: bufio.NewReader(rd)}
}
func NewReaderSize(rd io.Reader, size int) *Reader {
return &Reader{br: bufio.NewReaderSize(rd, size)}
}
func (b *Reader) ReadRune() (r rune, w int, err error) {
// if there's one in the bag, return that, and empty the bag
if b.peek.valid() {
r, w = b.peek.get()
b.peek.invalidate()
} else {
// otherwise, do a read
r, w, err = b.read()
if err != nil {
return
}
}
b.last.set(r, w)
return
}
func (b *Reader) PeekRune() (r rune, w int, err error) {
// if there's one in the bag, return that
if b.peek.valid() {
r, w = b.peek.get()
} else {
// otherwise, do a read and put in the bag
r, w, err = b.read()
if err != nil {
return
}
b.peek.set(r, w)
}
return r, w, nil
}
func (b *Reader) UnreadRune() error {
if b.last.invalid() {
return ErrInvalidUnreadRune
}
if b.peek.valid() {
// this kicks bad bad news at end of stream
// (i.e. not a simple EOF)
// so deliberately ignore its error
b.br.UnreadRune()
// err := b.br.UnreadRune()
// if err != io.EOF {
// return err
// }
}
b.peek = b.last
b.last.invalidate()
return nil
}
func (b *Reader) read() (rune, int, error) {
// read next rune from underlying reader, masking EOF by converting it from an error to rune value of -1.
r, w, err := b.br.ReadRune()
if err == io.EOF {
return EOF, 0, nil
} else if err != nil {
return 0, 0, err
}
return r, w, nil
}
// helper struct holding rune value and size
type runeCache struct {
ru rune
sz int
}
// all of these runeCache helpers will get inlined
func (r *runeCache) valid() bool {
return r.ru != 0
}
func (r *runeCache) invalid() bool {
return r.ru == 0
}
func (r *runeCache) invalidate() {
r.ru = 0
}
func (r *runeCache) get() (rune, int) {
return r.ru, r.sz
}
func (r *runeCache) set(ru rune, sz int) {
r.ru, r.sz = ru, sz
}