-
-
Notifications
You must be signed in to change notification settings - Fork 110
/
pointer.go
388 lines (352 loc) · 9.25 KB
/
pointer.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
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
package capnp
import (
"bytes"
"capnproto.org/go/capnp/v3/exc"
"capnproto.org/go/capnp/v3/internal/str"
)
// A Ptr is a reference to a Cap'n Proto struct, list, or interface.
// The zero value is a null pointer.
type Ptr struct {
seg *Segment
off address
lenOrCap uint32
size ObjectSize
depthLimit uint
flags ptrFlags
}
// Struct converts p to a Struct. If p does not hold a Struct pointer,
// the zero value is returned.
func (p Ptr) Struct() Struct {
if p.flags.ptrType() != structPtrType {
return Struct{}
}
return Struct{
seg: p.seg,
off: p.off,
size: p.size,
flags: p.flags.structFlags(),
depthLimit: p.depthLimit,
}
}
// StructDefault attempts to convert p into a struct, reading the
// default value from def if p is not a struct.
func (p Ptr) StructDefault(def []byte) (Struct, error) {
s := p.Struct()
if s.seg == nil {
if def == nil {
return Struct{}, nil
}
defp, err := unmarshalDefault(def)
if err != nil {
return Struct{}, err
}
return defp.Struct(), nil
}
return s, nil
}
// List converts p to a List. If p does not hold a List pointer,
// the zero value is returned.
func (p Ptr) List() List {
if p.flags.ptrType() != listPtrType {
return List{}
}
return List{
seg: p.seg,
off: p.off,
length: int32(p.lenOrCap),
size: p.size,
flags: p.flags.listFlags(),
depthLimit: p.depthLimit,
}
}
// ListDefault attempts to convert p into a list, reading the default
// value from def if p is not a list.
func (p Ptr) ListDefault(def []byte) (List, error) {
l := p.List()
if l.seg == nil {
if def == nil {
return List{}, nil
}
defp, err := unmarshalDefault(def)
if err != nil {
return List{}, err
}
return defp.List(), nil
}
return l, nil
}
// Interface converts p to an Interface. If p does not hold a List
// pointer, the zero value is returned.
func (p Ptr) Interface() Interface {
if p.flags.ptrType() != interfacePtrType {
return Interface{}
}
return Interface{
seg: p.seg,
cap: CapabilityID(p.lenOrCap),
}
}
// Text attempts to convert p into Text, returning an empty string if
// p is not a valid 1-byte list pointer.
func (p Ptr) Text() string {
b, ok := p.text()
if !ok {
return ""
}
return string(b)
}
// TextDefault attempts to convert p into Text, returning def if p is
// not a valid 1-byte list pointer.
func (p Ptr) TextDefault(def string) string {
b, ok := p.text()
if !ok {
return def
}
return string(b)
}
// TextBytes attempts to convert p into Text, returning nil if p is not
// a valid 1-byte list pointer. It returns a slice directly into the
// segment.
func (p Ptr) TextBytes() []byte {
b, ok := p.text()
if !ok {
return nil
}
return b
}
// TextBytesDefault attempts to convert p into Text, returning def if p
// is not a valid 1-byte list pointer. It returns a slice directly into
// the segment.
func (p Ptr) TextBytesDefault(def string) []byte {
b, ok := p.text()
if !ok {
return []byte(def)
}
return b
}
func (p Ptr) text() (b []byte, ok bool) {
if !isOneByteList(p) {
return nil, false
}
l := p.List()
b = l.seg.slice(l.off, Size(l.length))
if len(b) == 0 || b[len(b)-1] != 0 {
// Text must be null-terminated.
return nil, false
}
return b[: len(b)-1 : len(b)], true
}
// Data attempts to convert p into Data, returning nil if p is not a
// valid 1-byte list pointer.
func (p Ptr) Data() []byte {
return p.DataDefault(nil)
}
// DataDefault attempts to convert p into Data, returning def if p is
// not a valid 1-byte list pointer.
func (p Ptr) DataDefault(def []byte) []byte {
if !isOneByteList(p) {
return def
}
l := p.List()
b := l.seg.slice(l.off, Size(l.length))
if b == nil {
return def
}
return b
}
// IsValid reports whether p is valid.
func (p Ptr) IsValid() bool {
return p.seg != nil
}
// Segment returns the segment that the referenced data is stored in
// or nil if the pointer is invalid.
func (p Ptr) Segment() *Segment {
return p.seg
}
// Message returns the message the referenced data is stored in or nil
// if the pointer is invalid.
func (p Ptr) Message() *Message {
if p.seg == nil {
return nil
}
return p.seg.Message()
}
// Default returns p if it is valid, otherwise it unmarshals def.
func (p Ptr) Default(def []byte) (Ptr, error) {
if !p.IsValid() {
return unmarshalDefault(def)
}
return p, nil
}
// SamePtr reports whether p and q refer to the same object.
func SamePtr(p, q Ptr) bool {
return p.seg == q.seg && p.off == q.off
}
// EncodeAsPtr returns the receiver; for implementing TypeParam.
// The segment argument is ignored.
func (p Ptr) EncodeAsPtr(*Segment) Ptr { return p }
// DecodeFromPtr returns its argument; for implementing TypeParam.
func (Ptr) DecodeFromPtr(p Ptr) Ptr { return p }
var _ TypeParam[Ptr] = Ptr{}
func unmarshalDefault(def []byte) (Ptr, error) {
msg, err := Unmarshal(def)
if err != nil {
return Ptr{}, exc.WrapError("read default", err)
}
p, err := msg.Root()
if err != nil {
return Ptr{}, exc.WrapError("read default", err)
}
return p, nil
}
type ptrFlags uint8
const interfacePtrFlag ptrFlags = interfacePtrType << 6
func structPtrFlag(f structFlags) ptrFlags {
return structPtrType<<6 | ptrFlags(f)&ptrLowerMask
}
func listPtrFlag(f listFlags) ptrFlags {
return listPtrType<<6 | ptrFlags(f)&ptrLowerMask
}
const (
structPtrType = iota
listPtrType
interfacePtrType
)
func (f ptrFlags) ptrType() int {
return int(f >> 6)
}
const ptrLowerMask ptrFlags = 0x3f
func (f ptrFlags) listFlags() listFlags {
return listFlags(f & ptrLowerMask)
}
func (f ptrFlags) structFlags() structFlags {
return structFlags(f & ptrLowerMask)
}
func isZeroFilled(b []byte) bool {
for _, bb := range b {
if bb != 0 {
return false
}
}
return true
}
// Equal returns true iff p1 and p2 are equal.
//
// Equality is defined to be:
//
// - Two structs are equal iff all of their fields are equal. If one
// struct has more fields than the other, the extra fields must all be
// zero.
// - Two lists are equal iff they have the same length and their
// corresponding elements are equal. If one list is a list of
// primitives and the other is a list of structs, then the list of
// primitives is treated as if it was a list of structs with the
// element value as the sole field.
// - Two interfaces are equal iff they point to a capability created by
// the same call to NewClient or they are referring to the same
// capability table index in the same message. The latter is
// significant when the message's capability table has not been
// populated.
// - Two null pointers are equal.
// - All other combinations of things are not equal.
func Equal(p1, p2 Ptr) (bool, error) {
if !p1.IsValid() && !p2.IsValid() {
return true, nil
}
if !p1.IsValid() || !p2.IsValid() {
return false, nil
}
pt := p1.flags.ptrType()
if pt != p2.flags.ptrType() {
return false, nil
}
switch pt {
case structPtrType:
s1, s2 := p1.Struct(), p2.Struct()
data1 := s1.seg.slice(s1.off, s1.size.DataSize)
data2 := s2.seg.slice(s2.off, s2.size.DataSize)
switch {
case len(data1) < len(data2):
if !bytes.Equal(data1, data2[:len(data1)]) {
return false, nil
}
if !isZeroFilled(data2[len(data1):]) {
return false, nil
}
case len(data1) > len(data2):
if !bytes.Equal(data1[:len(data2)], data2) {
return false, nil
}
if !isZeroFilled(data1[len(data2):]) {
return false, nil
}
default:
if !bytes.Equal(data1, data2) {
return false, nil
}
}
n := int(s1.size.PointerCount)
if n2 := int(s2.size.PointerCount); n2 < n {
n = n2
}
for i := 0; i < n; i++ {
sp1, err := s1.Ptr(uint16(i))
if err != nil {
return false, exc.WrapError("equal", err)
}
sp2, err := s2.Ptr(uint16(i))
if err != nil {
return false, exc.WrapError("equal", err)
}
if ok, err := Equal(sp1, sp2); !ok || err != nil {
return false, err
}
}
for i := n; i < int(s1.size.PointerCount); i++ {
if s1.HasPtr(uint16(i)) {
return false, nil
}
}
for i := n; i < int(s2.size.PointerCount); i++ {
if s2.HasPtr(uint16(i)) {
return false, nil
}
}
return true, nil
case listPtrType:
l1, l2 := p1.List(), p2.List()
if l1.Len() != l2.Len() {
return false, nil
}
if l1.flags&isCompositeList == 0 && l2.flags&isCompositeList == 0 && l1.size != l2.size {
return false, nil
}
if l1.size.PointerCount == 0 && l2.size.PointerCount == 0 && l1.size.DataSize == l2.size.DataSize {
// Optimization: pure data lists can be compared bytewise.
sz, _ := l1.size.totalSize().times(l1.length) // both list bounds have been validated
return bytes.Equal(l1.seg.slice(l1.off, sz), l2.seg.slice(l2.off, sz)), nil
}
for i := 0; i < l1.Len(); i++ {
e1, e2 := l1.Struct(i), l2.Struct(i)
if ok, err := Equal(e1.ToPtr(), e2.ToPtr()); err != nil {
return false, exc.WrapError("equal: list element "+str.Itod(i), err)
} else if !ok {
return false, nil
}
}
return true, nil
case interfacePtrType:
i1, i2 := p1.Interface(), p2.Interface()
if i1.Message() == i2.Message() {
if i1.Capability() == i2.Capability() {
return true, nil
}
if !i1.Message().CapTable().Contains(i1) || !i1.Message().CapTable().Contains(i2) {
return false, nil
}
}
return i1.Client().IsSame(i2.Client()), nil
default:
panic("unreachable")
}
}