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
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
|
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package protopack enables manual encoding and decoding of protobuf wire data.
//
// This package is intended for use in debugging and/or creation of test data.
// Proper usage of this package requires knowledge of the wire format.
//
// See https://protobuf.dev/programming-guides/encoding.
package protopack
import (
"fmt"
"io"
"math"
"path"
"reflect"
"strconv"
"strings"
"unicode"
"unicode/utf8"
"google.golang.org/protobuf/encoding/protowire"
"google.golang.org/protobuf/reflect/protoreflect"
)
// Number is the field number; aliased from the protowire package for convenience.
type Number = protowire.Number
// Number type constants; copied from the protowire package for convenience.
const (
MinValidNumber Number = protowire.MinValidNumber
FirstReservedNumber Number = protowire.FirstReservedNumber
LastReservedNumber Number = protowire.LastReservedNumber
MaxValidNumber Number = protowire.MaxValidNumber
)
// Type is the wire type; aliased from the protowire package for convenience.
type Type = protowire.Type
// Wire type constants; copied from the protowire package for convenience.
const (
VarintType Type = protowire.VarintType
Fixed32Type Type = protowire.Fixed32Type
Fixed64Type Type = protowire.Fixed64Type
BytesType Type = protowire.BytesType
StartGroupType Type = protowire.StartGroupType
EndGroupType Type = protowire.EndGroupType
)
type (
// Token is any other type (e.g., Message, Tag, Varint, Float32, etc).
Token token
// Message is an ordered sequence of Tokens, where certain tokens may
// contain other tokens. It is functionally a concrete syntax tree that
// losslessly represents any arbitrary wire data (including invalid input).
Message []Token
// Tag is a tuple of the field number and the wire type.
Tag struct {
Number Number
Type Type
}
// Bool is a boolean.
Bool bool
// Varint is a signed varint using 64-bit two's complement encoding.
Varint int64
// Svarint is a signed varint using zig-zag encoding.
Svarint int64
// Uvarint is a unsigned varint.
Uvarint uint64
// Int32 is a signed 32-bit fixed-width integer.
Int32 int32
// Uint32 is an unsigned 32-bit fixed-width integer.
Uint32 uint32
// Float32 is a 32-bit fixed-width floating point number.
Float32 float32
// Int64 is a signed 64-bit fixed-width integer.
Int64 int64
// Uint64 is an unsigned 64-bit fixed-width integer.
Uint64 uint64
// Float64 is a 64-bit fixed-width floating point number.
Float64 float64
// String is a length-prefixed string.
String string
// Bytes is a length-prefixed bytes.
Bytes []byte
// LengthPrefix is a length-prefixed message.
LengthPrefix Message
// Denormalized is a denormalized varint value, where a varint is encoded
// using more bytes than is strictly necessary. The number of extra bytes
// alone is sufficient to losslessly represent the denormalized varint.
//
// The value may be one of Tag, Bool, Varint, Svarint, or Uvarint,
// where the varint representation of each token is denormalized.
//
// Alternatively, the value may be one of String, Bytes, or LengthPrefix,
// where the varint representation of the length-prefix is denormalized.
Denormalized struct {
Count uint // number of extra bytes
Value Token
}
// Raw are bytes directly appended to output.
Raw []byte
)
type token interface {
isToken()
}
func (Message) isToken() {}
func (Tag) isToken() {}
func (Bool) isToken() {}
func (Varint) isToken() {}
func (Svarint) isToken() {}
func (Uvarint) isToken() {}
func (Int32) isToken() {}
func (Uint32) isToken() {}
func (Float32) isToken() {}
func (Int64) isToken() {}
func (Uint64) isToken() {}
func (Float64) isToken() {}
func (String) isToken() {}
func (Bytes) isToken() {}
func (LengthPrefix) isToken() {}
func (Denormalized) isToken() {}
func (Raw) isToken() {}
// Size reports the size in bytes of the marshaled message.
func (m Message) Size() int {
var n int
for _, v := range m {
switch v := v.(type) {
case Message:
n += v.Size()
case Tag:
n += protowire.SizeTag(v.Number)
case Bool:
n += protowire.SizeVarint(protowire.EncodeBool(false))
case Varint:
n += protowire.SizeVarint(uint64(v))
case Svarint:
n += protowire.SizeVarint(protowire.EncodeZigZag(int64(v)))
case Uvarint:
n += protowire.SizeVarint(uint64(v))
case Int32, Uint32, Float32:
n += protowire.SizeFixed32()
case Int64, Uint64, Float64:
n += protowire.SizeFixed64()
case String:
n += protowire.SizeBytes(len(v))
case Bytes:
n += protowire.SizeBytes(len(v))
case LengthPrefix:
n += protowire.SizeBytes(Message(v).Size())
case Denormalized:
n += int(v.Count) + Message{v.Value}.Size()
case Raw:
n += len(v)
default:
panic(fmt.Sprintf("unknown type: %T", v))
}
}
return n
}
// Marshal encodes a syntax tree into the protobuf wire format.
//
// Example message definition:
//
// message MyMessage {
// string field1 = 1;
// int64 field2 = 2;
// repeated float32 field3 = 3;
// }
//
// Example encoded message:
//
// b := Message{
// Tag{1, BytesType}, String("Hello, world!"),
// Tag{2, VarintType}, Varint(-10),
// Tag{3, BytesType}, LengthPrefix{
// Float32(1.1), Float32(2.2), Float32(3.3),
// },
// }.Marshal()
//
// Resulting wire data:
//
// 0x0000 0a 0d 48 65 6c 6c 6f 2c 20 77 6f 72 6c 64 21 10 |..Hello, world!.|
// 0x0010 f6 ff ff ff ff ff ff ff ff 01 1a 0c cd cc 8c 3f |...............?|
// 0x0020 cd cc 0c 40 33 33 53 40 |...@33S@|
func (m Message) Marshal() []byte {
var out []byte
for _, v := range m {
switch v := v.(type) {
case Message:
out = append(out, v.Marshal()...)
case Tag:
out = protowire.AppendTag(out, v.Number, v.Type)
case Bool:
out = protowire.AppendVarint(out, protowire.EncodeBool(bool(v)))
case Varint:
out = protowire.AppendVarint(out, uint64(v))
case Svarint:
out = protowire.AppendVarint(out, protowire.EncodeZigZag(int64(v)))
case Uvarint:
out = protowire.AppendVarint(out, uint64(v))
case Int32:
out = protowire.AppendFixed32(out, uint32(v))
case Uint32:
out = protowire.AppendFixed32(out, uint32(v))
case Float32:
out = protowire.AppendFixed32(out, math.Float32bits(float32(v)))
case Int64:
out = protowire.AppendFixed64(out, uint64(v))
case Uint64:
out = protowire.AppendFixed64(out, uint64(v))
case Float64:
out = protowire.AppendFixed64(out, math.Float64bits(float64(v)))
case String:
out = protowire.AppendBytes(out, []byte(v))
case Bytes:
out = protowire.AppendBytes(out, []byte(v))
case LengthPrefix:
out = protowire.AppendBytes(out, Message(v).Marshal())
case Denormalized:
b := Message{v.Value}.Marshal()
_, n := protowire.ConsumeVarint(b)
out = append(out, b[:n]...)
for i := uint(0); i < v.Count; i++ {
out[len(out)-1] |= 0x80 // set continuation bit on previous
out = append(out, 0)
}
out = append(out, b[n:]...)
case Raw:
return append(out, v...)
default:
panic(fmt.Sprintf("unknown type: %T", v))
}
}
return out
}
// Unmarshal parses the input protobuf wire data as a syntax tree.
// Any parsing error results in the remainder of the input being
// concatenated to the message as a Raw type.
//
// Each tag (a tuple of the field number and wire type) encountered is
// inserted into the syntax tree as a Tag.
//
// The contents of each wire type is mapped to the following Go types:
//
// VarintType => Uvarint
// Fixed32Type => Uint32
// Fixed64Type => Uint64
// BytesType => Bytes
// GroupType => Message
//
// Since the wire format is not self-describing, this function cannot parse
// sub-messages and will leave them as the Bytes type. Further manual parsing
// can be performed as such:
//
// var m, m1, m2 Message
// m.Unmarshal(b)
// m1.Unmarshal(m[3].(Bytes))
// m[3] = LengthPrefix(m1)
// m2.Unmarshal(m[3].(LengthPrefix)[1].(Bytes))
// m[3].(LengthPrefix)[1] = LengthPrefix(m2)
//
// Unmarshal is useful for debugging the protobuf wire format.
func (m *Message) Unmarshal(in []byte) {
m.unmarshal(in, nil, false)
}
// UnmarshalDescriptor parses the input protobuf wire data as a syntax tree
// using the provided message descriptor for more accurate parsing of fields.
// It operates like Unmarshal, but may use a wider range of Go types to
// represent the wire data.
//
// The contents of each wire type is mapped to one of the following Go types:
//
// VarintType => Bool, Varint, Svarint, Uvarint
// Fixed32Type => Int32, Uint32, Float32
// Fixed64Type => Uint32, Uint64, Float64
// BytesType => String, Bytes, LengthPrefix
// GroupType => Message
//
// If the field is unknown, it uses the same mapping as Unmarshal.
// Known sub-messages are parsed as a Message and packed repeated fields are
// parsed as a LengthPrefix.
func (m *Message) UnmarshalDescriptor(in []byte, desc protoreflect.MessageDescriptor) {
m.unmarshal(in, desc, false)
}
// UnmarshalAbductive is like UnmarshalDescriptor, but infers abductively
// whether any unknown bytes values is a message based on whether it is
// a syntactically well-formed message.
//
// Note that the protobuf wire format is not fully self-describing,
// so abductive inference may attempt to expand a bytes value as a message
// that is not actually a message. It is a best-effort guess.
func (m *Message) UnmarshalAbductive(in []byte, desc protoreflect.MessageDescriptor) {
m.unmarshal(in, desc, true)
}
func (m *Message) unmarshal(in []byte, desc protoreflect.MessageDescriptor, inferMessage bool) {
p := parser{in: in, out: *m}
p.parseMessage(desc, false, inferMessage)
*m = p.out
}
type parser struct {
in []byte
out []Token
invalid bool
}
func (p *parser) parseMessage(msgDesc protoreflect.MessageDescriptor, group, inferMessage bool) {
for len(p.in) > 0 {
v, n := protowire.ConsumeVarint(p.in)
num, typ := protowire.DecodeTag(v)
if n < 0 || num <= 0 || v > math.MaxUint32 {
p.out, p.in = append(p.out, Raw(p.in)), nil
p.invalid = true
return
}
if typ == EndGroupType && group {
return // if inside a group, then stop
}
p.out, p.in = append(p.out, Tag{num, typ}), p.in[n:]
if m := n - protowire.SizeVarint(v); m > 0 {
p.out[len(p.out)-1] = Denormalized{uint(m), p.out[len(p.out)-1]}
}
// If descriptor is available, use it for more accurate parsing.
var isPacked bool
var kind protoreflect.Kind
var subDesc protoreflect.MessageDescriptor
if msgDesc != nil && !msgDesc.IsPlaceholder() {
if fieldDesc := msgDesc.Fields().ByNumber(num); fieldDesc != nil {
isPacked = fieldDesc.IsPacked()
kind = fieldDesc.Kind()
switch kind {
case protoreflect.MessageKind, protoreflect.GroupKind:
subDesc = fieldDesc.Message()
if subDesc == nil || subDesc.IsPlaceholder() {
kind = 0
}
}
}
}
switch typ {
case VarintType:
p.parseVarint(kind)
case Fixed32Type:
p.parseFixed32(kind)
case Fixed64Type:
p.parseFixed64(kind)
case BytesType:
p.parseBytes(isPacked, kind, subDesc, inferMessage)
case StartGroupType:
p.parseGroup(num, subDesc, inferMessage)
case EndGroupType:
// Handled by p.parseGroup.
default:
p.out, p.in = append(p.out, Raw(p.in)), nil
p.invalid = true
}
}
}
func (p *parser) parseVarint(kind protoreflect.Kind) {
v, n := protowire.ConsumeVarint(p.in)
if n < 0 {
p.out, p.in = append(p.out, Raw(p.in)), nil
p.invalid = true
return
}
switch kind {
case protoreflect.BoolKind:
switch v {
case 0:
p.out, p.in = append(p.out, Bool(false)), p.in[n:]
case 1:
p.out, p.in = append(p.out, Bool(true)), p.in[n:]
default:
p.out, p.in = append(p.out, Uvarint(v)), p.in[n:]
}
case protoreflect.Int32Kind, protoreflect.Int64Kind:
p.out, p.in = append(p.out, Varint(v)), p.in[n:]
case protoreflect.Sint32Kind, protoreflect.Sint64Kind:
p.out, p.in = append(p.out, Svarint(protowire.DecodeZigZag(v))), p.in[n:]
default:
p.out, p.in = append(p.out, Uvarint(v)), p.in[n:]
}
if m := n - protowire.SizeVarint(v); m > 0 {
p.out[len(p.out)-1] = Denormalized{uint(m), p.out[len(p.out)-1]}
}
}
func (p *parser) parseFixed32(kind protoreflect.Kind) {
v, n := protowire.ConsumeFixed32(p.in)
if n < 0 {
p.out, p.in = append(p.out, Raw(p.in)), nil
p.invalid = true
return
}
switch kind {
case protoreflect.FloatKind:
p.out, p.in = append(p.out, Float32(math.Float32frombits(v))), p.in[n:]
case protoreflect.Sfixed32Kind:
p.out, p.in = append(p.out, Int32(v)), p.in[n:]
default:
p.out, p.in = append(p.out, Uint32(v)), p.in[n:]
}
}
func (p *parser) parseFixed64(kind protoreflect.Kind) {
v, n := protowire.ConsumeFixed64(p.in)
if n < 0 {
p.out, p.in = append(p.out, Raw(p.in)), nil
p.invalid = true
return
}
switch kind {
case protoreflect.DoubleKind:
p.out, p.in = append(p.out, Float64(math.Float64frombits(v))), p.in[n:]
case protoreflect.Sfixed64Kind:
p.out, p.in = append(p.out, Int64(v)), p.in[n:]
default:
p.out, p.in = append(p.out, Uint64(v)), p.in[n:]
}
}
func (p *parser) parseBytes(isPacked bool, kind protoreflect.Kind, desc protoreflect.MessageDescriptor, inferMessage bool) {
v, n := protowire.ConsumeVarint(p.in)
if n < 0 {
p.out, p.in = append(p.out, Raw(p.in)), nil
p.invalid = true
return
}
p.out, p.in = append(p.out, Uvarint(v)), p.in[n:]
if m := n - protowire.SizeVarint(v); m > 0 {
p.out[len(p.out)-1] = Denormalized{uint(m), p.out[len(p.out)-1]}
}
if v > uint64(len(p.in)) {
p.out, p.in = append(p.out, Raw(p.in)), nil
p.invalid = true
return
}
p.out = p.out[:len(p.out)-1] // subsequent tokens contain prefix-length
if isPacked {
p.parsePacked(int(v), kind)
} else {
switch kind {
case protoreflect.MessageKind:
p2 := parser{in: p.in[:v]}
p2.parseMessage(desc, false, inferMessage)
p.out, p.in = append(p.out, LengthPrefix(p2.out)), p.in[v:]
case protoreflect.StringKind:
p.out, p.in = append(p.out, String(p.in[:v])), p.in[v:]
case protoreflect.BytesKind:
p.out, p.in = append(p.out, Bytes(p.in[:v])), p.in[v:]
default:
if inferMessage {
// Check whether this is a syntactically valid message.
p2 := parser{in: p.in[:v]}
p2.parseMessage(nil, false, inferMessage)
if !p2.invalid {
p.out, p.in = append(p.out, LengthPrefix(p2.out)), p.in[v:]
break
}
}
p.out, p.in = append(p.out, Bytes(p.in[:v])), p.in[v:]
}
}
if m := n - protowire.SizeVarint(v); m > 0 {
p.out[len(p.out)-1] = Denormalized{uint(m), p.out[len(p.out)-1]}
}
}
func (p *parser) parsePacked(n int, kind protoreflect.Kind) {
p2 := parser{in: p.in[:n]}
for len(p2.in) > 0 {
switch kind {
case protoreflect.BoolKind, protoreflect.EnumKind,
protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Uint32Kind,
protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Uint64Kind:
p2.parseVarint(kind)
case protoreflect.Fixed32Kind, protoreflect.Sfixed32Kind, protoreflect.FloatKind:
p2.parseFixed32(kind)
case protoreflect.Fixed64Kind, protoreflect.Sfixed64Kind, protoreflect.DoubleKind:
p2.parseFixed64(kind)
default:
panic(fmt.Sprintf("invalid packed kind: %v", kind))
}
}
p.out, p.in = append(p.out, LengthPrefix(p2.out)), p.in[n:]
}
func (p *parser) parseGroup(startNum protowire.Number, desc protoreflect.MessageDescriptor, inferMessage bool) {
p2 := parser{in: p.in}
p2.parseMessage(desc, true, inferMessage)
if len(p2.out) > 0 {
p.out = append(p.out, Message(p2.out))
}
p.in = p2.in
// Append the trailing end group.
v, n := protowire.ConsumeVarint(p.in)
if endNum, typ := protowire.DecodeTag(v); typ == EndGroupType {
if startNum != endNum {
p.invalid = true
}
p.out, p.in = append(p.out, Tag{endNum, typ}), p.in[n:]
if m := n - protowire.SizeVarint(v); m > 0 {
p.out[len(p.out)-1] = Denormalized{uint(m), p.out[len(p.out)-1]}
}
}
}
// Format implements a custom formatter to visualize the syntax tree.
// Using "%#v" formats the Message in Go source code.
func (m Message) Format(s fmt.State, r rune) {
switch r {
case 'x':
io.WriteString(s, fmt.Sprintf("%x", m.Marshal()))
case 'X':
io.WriteString(s, fmt.Sprintf("%X", m.Marshal()))
case 'v':
switch {
case s.Flag('#'):
io.WriteString(s, m.format(true, true))
case s.Flag('+'):
io.WriteString(s, m.format(false, true))
default:
io.WriteString(s, m.format(false, false))
}
default:
panic("invalid verb: " + string(r))
}
}
// format formats the message.
// If source is enabled, this emits valid Go source.
// If multi is enabled, the output may span multiple lines.
func (m Message) format(source, multi bool) string {
var ss []string
var prefix, nextPrefix string
for _, v := range m {
// Ensure certain tokens have preceding or succeeding newlines.
prefix, nextPrefix = nextPrefix, " "
if multi {
switch v := v.(type) {
case Tag: // only has preceding newline
prefix = "\n"
case Denormalized: // only has preceding newline
if _, ok := v.Value.(Tag); ok {
prefix = "\n"
}
case Message, Raw: // has preceding and succeeding newlines
prefix, nextPrefix = "\n", "\n"
}
}
s := formatToken(v, source, multi)
ss = append(ss, prefix+s+",")
}
var s string
if len(ss) > 0 {
s = strings.TrimSpace(strings.Join(ss, ""))
if multi {
s = "\n\t" + strings.Join(strings.Split(s, "\n"), "\n\t") + "\n"
} else {
s = strings.TrimSuffix(s, ",")
}
}
s = fmt.Sprintf("%T{%s}", m, s)
if !source {
s = trimPackage(s)
}
return s
}
// formatToken formats a single token.
func formatToken(t Token, source, multi bool) (s string) {
switch v := t.(type) {
case Message:
s = v.format(source, multi)
case LengthPrefix:
s = formatPacked(v, source, multi)
if s == "" {
ms := Message(v).format(source, multi)
s = fmt.Sprintf("%T(%s)", v, ms)
}
case Tag:
s = fmt.Sprintf("%T{%d, %s}", v, v.Number, formatType(v.Type, source))
case Bool, Varint, Svarint, Uvarint, Int32, Uint32, Float32, Int64, Uint64, Float64:
if source {
// Print floats in a way that preserves exact precision.
if f, _ := v.(Float32); math.IsNaN(float64(f)) || math.IsInf(float64(f), 0) {
switch {
case f > 0:
s = fmt.Sprintf("%T(math.Inf(+1))", v)
case f < 0:
s = fmt.Sprintf("%T(math.Inf(-1))", v)
case math.Float32bits(float32(math.NaN())) == math.Float32bits(float32(f)):
s = fmt.Sprintf("%T(math.NaN())", v)
default:
s = fmt.Sprintf("%T(math.Float32frombits(0x%08x))", v, math.Float32bits(float32(f)))
}
break
}
if f, _ := v.(Float64); math.IsNaN(float64(f)) || math.IsInf(float64(f), 0) {
switch {
case f > 0:
s = fmt.Sprintf("%T(math.Inf(+1))", v)
case f < 0:
s = fmt.Sprintf("%T(math.Inf(-1))", v)
case math.Float64bits(float64(math.NaN())) == math.Float64bits(float64(f)):
s = fmt.Sprintf("%T(math.NaN())", v)
default:
s = fmt.Sprintf("%T(math.Float64frombits(0x%016x))", v, math.Float64bits(float64(f)))
}
break
}
}
s = fmt.Sprintf("%T(%v)", v, v)
case String, Bytes, Raw:
s = fmt.Sprintf("%s", v)
s = fmt.Sprintf("%T(%s)", v, formatString(s))
case Denormalized:
s = fmt.Sprintf("%T{+%d, %v}", v, v.Count, formatToken(v.Value, source, multi))
default:
panic(fmt.Sprintf("unknown type: %T", v))
}
if !source {
s = trimPackage(s)
}
return s
}
// formatPacked returns a non-empty string if LengthPrefix looks like a packed
// repeated field of primitives.
func formatPacked(v LengthPrefix, source, multi bool) string {
var ss []string
for _, v := range v {
switch v.(type) {
case Bool, Varint, Svarint, Uvarint, Int32, Uint32, Float32, Int64, Uint64, Float64, Denormalized, Raw:
if v, ok := v.(Denormalized); ok {
switch v.Value.(type) {
case Bool, Varint, Svarint, Uvarint:
default:
return ""
}
}
ss = append(ss, formatToken(v, source, multi))
default:
return ""
}
}
s := fmt.Sprintf("%T{%s}", v, strings.Join(ss, ", "))
if !source {
s = trimPackage(s)
}
return s
}
// formatType returns the name for Type.
func formatType(t Type, source bool) (s string) {
switch t {
case VarintType:
s = pkg + ".VarintType"
case Fixed32Type:
s = pkg + ".Fixed32Type"
case Fixed64Type:
s = pkg + ".Fixed64Type"
case BytesType:
s = pkg + ".BytesType"
case StartGroupType:
s = pkg + ".StartGroupType"
case EndGroupType:
s = pkg + ".EndGroupType"
default:
s = fmt.Sprintf("Type(%d)", t)
}
if !source {
s = strings.TrimSuffix(trimPackage(s), "Type")
}
return s
}
// formatString returns a quoted string for s.
func formatString(s string) string {
// Use quoted string if it the same length as a raw string literal.
// Otherwise, attempt to use the raw string form.
qs := strconv.Quote(s)
if len(qs) == 1+len(s)+1 {
return qs
}
// Disallow newlines to ensure output is a single line.
// Disallow non-printable runes for readability purposes.
rawInvalid := func(r rune) bool {
return r == '`' || r == '\n' || r == utf8.RuneError || !unicode.IsPrint(r)
}
if strings.IndexFunc(s, rawInvalid) < 0 {
return "`" + s + "`"
}
return qs
}
var pkg = path.Base(reflect.TypeOf(Tag{}).PkgPath())
func trimPackage(s string) string {
return strings.TrimPrefix(strings.TrimPrefix(s, pkg), ".")
}
|