/
jsonEncoder.go
218 lines (203 loc) · 5.2 KB
/
jsonEncoder.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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
package json
import (
"fmt"
"io"
"strconv"
. "github.com/ipfs/fs-repo-migrations/ipfs-10-to-11/_vendor/github.com/polydawn/refmt/tok"
)
func NewEncoder(wr io.Writer, cfg EncodeOptions) *Encoder {
return &Encoder{
wr: wr,
cfg: cfg,
stack: make([]phase, 0, 10),
}
}
func (d *Encoder) Reset() {
d.stack = d.stack[0:0]
d.current = phase_anyExpectValue
d.some = false
}
/*
A json.Encoder is a TokenSink implementation that emits json bytes.
*/
type Encoder struct {
wr io.Writer
cfg EncodeOptions
// Stack, tracking how many array and map opens are outstanding.
// (Values are only 'phase_mapExpectKeyOrEnd' and 'phase_arrExpectValueOrEnd'.)
stack []phase
current phase // shortcut to value at end of stack
some bool // set to true after first value in any context; use to append commas.
// Spare memory, for use in operations on leaf nodes (e.g. temp space for an int serialization).
scratch [64]byte
}
type phase int
const (
phase_anyExpectValue phase = iota
phase_mapExpectKeyOrEnd
phase_mapExpectValue
phase_arrExpectValueOrEnd
)
func (d *Encoder) Step(tok *Token) (done bool, err error) {
switch d.current {
case phase_anyExpectValue:
switch tok.Type {
case TMapOpen:
d.pushPhase(phase_mapExpectKeyOrEnd)
d.wr.Write(wordMapOpen)
return false, nil
case TArrOpen:
d.pushPhase(phase_arrExpectValueOrEnd)
d.wr.Write(wordArrOpen)
return false, nil
case TMapClose:
return true, fmt.Errorf("unexpected mapClose; expected start of value")
case TArrClose:
return true, fmt.Errorf("unexpected arrClose; expected start of value")
default:
// It's a value; handle it.
d.flushValue(tok)
return true, nil
}
case phase_mapExpectKeyOrEnd:
switch tok.Type {
case TMapOpen:
return true, fmt.Errorf("unexpected mapOpen; expected start of key or end of map")
case TArrOpen:
return true, fmt.Errorf("unexpected arrOpen; expected start of key or end of map")
case TMapClose:
if d.some {
d.wr.Write(d.cfg.Line)
for i := 1; i < len(d.stack); i++ {
d.wr.Write(d.cfg.Indent)
}
}
d.wr.Write(wordMapClose)
return d.popPhase()
case TArrClose:
return true, fmt.Errorf("unexpected arrClose; expected start of key or end of map")
default:
// It's a key. It'd better be a string.
switch tok.Type {
case TString:
d.entrySep()
d.emitString(tok.Str)
d.wr.Write(wordColon)
if d.cfg.Line != nil {
d.wr.Write(wordSpace)
}
d.current = phase_mapExpectValue
return false, nil
default:
return true, fmt.Errorf("unexpected token of type %T; expected map key", *tok)
}
}
case phase_mapExpectValue:
switch tok.Type {
case TMapOpen:
d.pushPhase(phase_mapExpectKeyOrEnd)
d.wr.Write(wordMapOpen)
return false, nil
case TArrOpen:
d.pushPhase(phase_arrExpectValueOrEnd)
d.wr.Write(wordArrOpen)
return false, nil
case TMapClose:
return true, fmt.Errorf("unexpected mapClose; expected start of value")
case TArrClose:
return true, fmt.Errorf("unexpected arrClose; expected start of value")
default:
// It's a value; handle it.
d.flushValue(tok)
d.current = phase_mapExpectKeyOrEnd
return false, nil
}
case phase_arrExpectValueOrEnd:
switch tok.Type {
case TMapOpen:
d.entrySep()
d.pushPhase(phase_mapExpectKeyOrEnd)
d.wr.Write(wordMapOpen)
return false, nil
case TArrOpen:
d.entrySep()
d.pushPhase(phase_arrExpectValueOrEnd)
d.wr.Write(wordArrOpen)
return false, nil
case TMapClose:
return true, fmt.Errorf("unexpected mapClose; expected start of value or end of array")
case TArrClose:
if d.some {
d.wr.Write(d.cfg.Line)
for i := 1; i < len(d.stack); i++ {
d.wr.Write(d.cfg.Indent)
}
}
d.wr.Write(wordArrClose)
return d.popPhase()
default:
// It's a value; handle it.
d.entrySep()
d.flushValue(tok)
return false, nil
}
default:
panic("Unreachable")
}
}
func (d *Encoder) pushPhase(p phase) {
d.current = p
d.stack = append(d.stack, d.current)
d.some = false
}
// Pop a phase from the stack; return 'true' if stack now empty.
func (d *Encoder) popPhase() (bool, error) {
n := len(d.stack) - 1
if n == 0 {
d.wr.Write(d.cfg.Line)
return true, nil
}
if n < 0 { // the state machines are supposed to have already errored better
panic("jsonEncoder stack overpopped")
}
d.current = d.stack[n-1]
d.stack = d.stack[0:n]
d.some = true
return false, nil
}
// Emit an entry separater (comma), unless we're at the start of an object.
// Mark that we *do* have some content, regardless, so next time will need a sep.
func (d *Encoder) entrySep() {
if d.some {
d.wr.Write(wordComma)
}
d.some = true
d.wr.Write(d.cfg.Line)
for i := 0; i < len(d.stack); i++ {
d.wr.Write(d.cfg.Indent)
}
}
func (d *Encoder) flushValue(tok *Token) {
switch tok.Type {
case TString:
d.emitString(tok.Str)
case TBool:
switch tok.Bool {
case true:
d.wr.Write(wordTrue)
case false:
d.wr.Write(wordFalse)
}
case TInt:
b := strconv.AppendInt(d.scratch[:0], tok.Int, 10)
d.wr.Write(b)
case TNull:
d.wr.Write(wordNull)
default:
panic(fmt.Errorf("TODO finish more jsonEncoder primitives support: unhandled token %s", tok))
}
}
func (d *Encoder) writeByte(b byte) {
d.scratch[0] = b
d.wr.Write(d.scratch[0:1])
}