-
Notifications
You must be signed in to change notification settings - Fork 14
/
marshalSlab.go
219 lines (194 loc) · 7.57 KB
/
marshalSlab.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
219
package obj
import (
"fmt"
"reflect"
"github.com/polydawn/refmt/obj/atlas"
. "github.com/polydawn/refmt/tok"
)
/*
A lovely mechanism to stash marshalMachine objects pre-allocated and avoid mallocs.
Works together with the Atlas: the Atlas says what kind of machinery is needed;
the marshalSlab "allocates" it and returns it upon your request.
*/
type marshalSlab struct {
atlas atlas.Atlas
rows []marshalSlabRow
}
type marshalSlabRow struct {
ptrDerefDelegateMarshalMachine
marshalMachinePrimitive
marshalMachineWildcard
marshalMachineMapWildcard
marshalMachineSliceWildcard
marshalMachineStructAtlas
marshalMachineTransform
marshalMachineUnionKeyed
errThunkMarshalMachine
}
// A thunk value that can be used to trigger `isNil` paths.
// (Substituting an 'invalid' kind reflect.Value with this is an easy way
// to emit a null without needing any additional special cases or error handling.)
var nil_rv reflect.Value = reflect.Zero(reflect.PtrTo(reflect.TypeOf(0)))
/*
Return a reference to a machine from the slab.
*You must release() when done.*
Errors -- including "no info in Atlas for this type" -- are expressed by
returning a machine that is a constantly-erroring thunk.
*/
func (slab *marshalSlab) requisitionMachine(rt reflect.Type) MarshalMachine {
// Acquire a row.
off := len(slab.rows)
slab.grow()
row := &slab.rows[off]
// Yield machinery.
return _yieldMarshalMachinePtr(row, slab.atlas, rt)
}
/*
Like requisitionMachine, but does *not* grow the slab; assumes the current
tip row is usable.
Thus, you must grow() before using, and release correspondingly.
*/
func (slab *marshalSlab) yieldMachine(rt reflect.Type) MarshalMachine {
// Grab the last row.
row := &slab.rows[len(slab.rows)-1]
// Yield machinery.
return _yieldMarshalMachinePtr(row, slab.atlas, rt)
}
func _yieldMarshalMachinePtr(row *marshalSlabRow, atl atlas.Atlas, rt reflect.Type) MarshalMachine {
// Indirect pointers as necessary.
// Keep count of how many times we do this; we'll use this again at the end.
peelCount := 0
for rt.Kind() == reflect.Ptr {
rt = rt.Elem()
peelCount++
}
// Figure out what machinery to use at heart.
mach := _yieldBareMarshalMachinePtr(row, atl, rt)
// If nil answer, we had no match: yield an error thunk.
if mach == nil {
mach := &row.errThunkMarshalMachine
mach.err = fmt.Errorf("no machine found")
return mach
}
// If no indirection steps, return;
// otherwise wrap it in the ptrDeref machine and return that.
if peelCount == 0 {
return mach
}
row.ptrDerefDelegateMarshalMachine.MarshalMachine = mach
row.ptrDerefDelegateMarshalMachine.peelCount = peelCount
row.ptrDerefDelegateMarshalMachine.isNil = false
return &row.ptrDerefDelegateMarshalMachine
}
// Like _yieldMarshalMachinePtr, but assumes the ptr unwrapping has already been done.
func _yieldBareMarshalMachinePtr(row *marshalSlabRow, atl atlas.Atlas, rt reflect.Type) MarshalMachine {
rtid := reflect.ValueOf(rt).Pointer()
// Check primitives first; cheapest (and unoverridable).
switch rtid {
case rtid_bool,
rtid_string,
rtid_int, rtid_int8, rtid_int16, rtid_int32, rtid_int64,
rtid_uint, rtid_uint8, rtid_uint16, rtid_uint32, rtid_uint64, rtid_uintptr,
rtid_float32, rtid_float64,
rtid_bytes:
row.marshalMachinePrimitive.kind = rt.Kind()
return &row.marshalMachinePrimitive
}
// Consult atlas second.
if entry, ok := atl.Get(rtid); ok {
return _yieldMarshalMachinePtrForAtlasEntry(row, entry, atl)
}
// If no specific behavior found, use default behavior based on kind.
switch rt.Kind() {
case reflect.Bool,
reflect.String,
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
reflect.Float32, reflect.Float64:
row.marshalMachinePrimitive.kind = rt.Kind()
return &row.marshalMachinePrimitive
case reflect.Slice:
// un-typedef'd byte slices were handled already, but a typedef'd one still gets gets treated like a special kind:
if rt.Elem().Kind() == reflect.Uint8 {
row.marshalMachinePrimitive.kind = rt.Kind()
return &row.marshalMachinePrimitive
}
return &row.marshalMachineSliceWildcard
case reflect.Array:
// arrays of bytes have a similar special case to slices for when they're typedefed.
if rt.Elem().Kind() == reflect.Uint8 {
row.marshalMachinePrimitive.kind = rt.Kind()
return &row.marshalMachinePrimitive
}
return &row.marshalMachineSliceWildcard.marshalMachineArrayWildcard
case reflect.Map:
row.marshalMachineMapWildcard.morphism = atl.GetDefaultMapMorphism()
return &row.marshalMachineMapWildcard
case reflect.Struct:
// TODO here we could also invoke automatic atlas autogen, if configured to be permitted
mach := &row.errThunkMarshalMachine
mach.err = fmt.Errorf("missing an atlas entry describing how to marshal type %v (and auto-atlasing for structs is not enabled)", rt)
return mach
case reflect.Interface:
return &row.marshalMachineWildcard
case reflect.Func:
panic(fmt.Errorf("functions cannot be marshalled!"))
case reflect.Ptr:
panic(fmt.Errorf("unreachable: ptrs must already be resolved"))
default:
panic(fmt.Errorf("excursion %s", rt.Kind()))
}
}
// given that we already have an atlasEntry in mind, yield a configured machine for it.
// it seems odd that this might still require a whole atlas, but tis so;
// some things (e.g. transform funcs) need to get additional machinery for delegation.
func _yieldMarshalMachinePtrForAtlasEntry(row *marshalSlabRow, entry *atlas.AtlasEntry, atl atlas.Atlas) MarshalMachine {
// Switch across which of the union of configurations is applicable.
switch {
case entry.MarshalTransformFunc != nil:
// Return a machine that calls the func(s), then later a real machine.
// The entry.MarshalTransformTargetType is used to do a recursive lookup.
// We can't just call the func here because we're still working off typeinfo
// and don't have a real value to transform until later.
row.marshalMachineTransform.trFunc = entry.MarshalTransformFunc
// Pick delegate without growing stack. (This currently means recursive transform won't fly.)
row.marshalMachineTransform.delegate = _yieldMarshalMachinePtr(row, atl, entry.MarshalTransformTargetType)
// If tags are in play: have the transformer machine glue that on.
row.marshalMachineTransform.tagged = entry.Tagged
row.marshalMachineTransform.tag = entry.Tag
return &row.marshalMachineTransform
case entry.StructMap != nil:
row.marshalMachineStructAtlas.cfg = entry
return &row.marshalMachineStructAtlas
case entry.UnionKeyedMorphism != nil:
row.marshalMachineUnionKeyed.cfg = entry
return &row.marshalMachineUnionKeyed
case entry.MapMorphism != nil:
row.marshalMachineMapWildcard.morphism = entry.MapMorphism
return &row.marshalMachineMapWildcard
default:
panic("invalid atlas entry")
}
}
// Returns the top row of the slab. Useful for machines that need to delegate
// to another type that's definitely not their own. Be careful with that
// caveat; if the delegation can be to another system that uses in-row delegation,
// this is not trivially safe to compose and you should grow the slab instead.
func (s *marshalSlab) tip() *marshalSlabRow {
return &s.rows[len(s.rows)-1]
}
func (s *marshalSlab) grow() {
s.rows = append(s.rows, marshalSlabRow{})
}
func (s *marshalSlab) release() {
s.rows = s.rows[0 : len(s.rows)-1]
}
type errThunkMarshalMachine struct {
err error
}
func (m *errThunkMarshalMachine) Reset(_ *marshalSlab, _ reflect.Value, _ reflect.Type) error {
return m.err
}
func (m *errThunkMarshalMachine) Step(d *Marshaller, s *marshalSlab, tok *Token) (done bool, err error) {
return true, m.err
}