-
Notifications
You must be signed in to change notification settings - Fork 0
/
rune_reader_unix.go
165 lines (147 loc) · 3.08 KB
/
rune_reader_unix.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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
// +build !windows,!plan9
package tty
import (
"fmt"
"os"
"syscall"
"github.com/u-root/u-root/cmds/core/elvish/sys"
)
const (
runeReaderChanSize int = 128
)
// runeReader reads a Unix file continuously, assemble the bytes it reads into
// runes (assuming UTF-8), and delivers them on a channel.
type runeReader struct {
file *os.File
rStop *os.File
wStop *os.File
stopChan chan struct{}
runeChan chan rune
errorChan chan error
debug bool
}
// newRuneReader creates a new runeReader from a file.
func newRuneReader(file *os.File) *runeReader {
rStop, wStop, err := os.Pipe()
if err != nil {
panic(err)
}
return &runeReader{
file,
rStop, wStop,
make(chan struct{}),
make(chan rune, runeReaderChanSize),
make(chan error),
false,
}
}
// Chan returns a channel onto which the runeReader writes the runes it reads.
func (ar *runeReader) Chan() <-chan rune {
return ar.runeChan
}
// ErrorChan returns a channel onto which the runeReader writes the errors it
// encounters.
func (ar *runeReader) ErrorChan() <-chan error {
return ar.errorChan
}
// Start starts the runeReader.
func (ar *runeReader) Start() {
go ar.run()
}
// run runs the runeReader. It blocks until Quit is called and should be called
// in a separate goroutine.
func (ar *runeReader) run() {
var buf [1]byte
for {
ready, err := sys.WaitForRead(ar.file, ar.rStop)
if err != nil {
if err == syscall.EINTR {
continue
}
ar.fatal(err)
return
}
if ready[1] {
// Consume the written byte
ar.rStop.Read(buf[:])
<-ar.stopChan
return
}
nr, err := ar.file.Read(buf[:])
if nr != 1 {
continue
} else if err != nil {
ar.fatal(err)
return
}
leader := buf[0]
var (
r rune
pending int
)
switch {
case leader>>7 == 0:
r = rune(leader)
case leader>>5 == 0x6:
r = rune(leader & 0x1f)
pending = 1
case leader>>4 == 0xe:
r = rune(leader & 0xf)
pending = 2
case leader>>3 == 0x1e:
r = rune(leader & 0x7)
pending = 3
}
if ar.debug {
fmt.Printf("leader 0x%x, pending %d, r = 0x%x\n", leader, pending, r)
}
for i := 0; i < pending; i++ {
nr, err := ar.file.Read(buf[:])
if nr != 1 {
r = 0xfffd
break
} else if err != nil {
ar.fatal(err)
return
}
r = r<<6 + rune(buf[0]&0x3f)
if ar.debug {
fmt.Printf(" got 0x%d, r = 0x%x\n", buf[0], r)
}
}
// Write rune to ch, unless termination is requested.
select {
case ar.runeChan <- r:
case <-ar.stopChan:
ar.rStop.Read(buf[:])
return
}
}
}
func (ar *runeReader) fatal(err error) {
var cBuf [1]byte
select {
case ar.errorChan <- err:
case <-ar.stopChan:
ar.rStop.Read(cBuf[:])
return
}
<-ar.stopChan
ar.rStop.Read(cBuf[:])
}
// Stop terminates the loop of Run.
func (ar *runeReader) Stop() {
_, err := ar.wStop.Write([]byte{'q'})
if err != nil {
panic(err)
}
ar.stopChan <- struct{}{}
}
// Close releases files and channels associated with the AsyncReader. It does
// not close the file used to create it.
func (ar *runeReader) Close() {
ar.rStop.Close()
ar.wStop.Close()
close(ar.stopChan)
close(ar.runeChan)
}