blob: 89601ba31b17a406c7a8bd697dcbc25b24ec95ee [file] [log] [blame]
Joe Tsai3ab648c2018-08-15 14:41:30 -07001// Copyright 2018 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// Package pack enables manual encoding and decoding of protobuf wire data.
6//
7// This package is intended for use in debugging and/or creation of test data.
8// Proper usage of this package requires knowledge of the wire format.
9//
10// See https://developers.google.com/protocol-buffers/docs/encoding.
11package pack
12
13import (
14 "fmt"
15 "io"
16 "math"
17 "path"
18 "reflect"
19 "strconv"
20 "strings"
21 "unicode"
22 "unicode/utf8"
23
24 "google.golang.org/proto/internal/encoding/wire"
25 "google.golang.org/proto/reflect/protoreflect"
26)
27
28// Number is the field number; aliased from the wire package for convenience.
29type Number = wire.Number
30
31// Number type constants; copied from the wire package for convenience.
32const (
33 MinValidNumber Number = wire.MinValidNumber
34 FirstReservedNumber Number = wire.FirstReservedNumber
35 LastReservedNumber Number = wire.LastReservedNumber
36 MaxValidNumber Number = wire.MaxValidNumber
37)
38
39// Type is the wire type; aliased from the wire package for convenience.
40type Type = wire.Type
41
42// Wire type constants; copied from the wire package for convenience.
43const (
44 VarintType Type = wire.VarintType
45 Fixed32Type Type = wire.Fixed32Type
46 Fixed64Type Type = wire.Fixed64Type
47 BytesType Type = wire.BytesType
48 StartGroupType Type = wire.StartGroupType
49 EndGroupType Type = wire.EndGroupType
50)
51
52type (
53 // Token is any other type (e.g., Message, Tag, Varint, Float32, etc).
54 Token token
55 // Message is a ordered sequence Tokens.
56 Message []Token
57
58 // Tag is a tuple of the field number and the wire type.
59 Tag struct {
60 Number Number
61 Type Type
62 }
63 // Bool is a boolean.
64 Bool bool
65 // Varint is a signed varint using 64-bit two's complement encoding.
66 Varint int64
67 // Svarint is a signed varint using zig-zag encoding.
68 Svarint int64
69 // Uvarint is a unsigned varint.
70 Uvarint uint64
71
72 // Int32 is a signed 32-bit fixed-width integer.
73 Int32 int32
74 // Uint32 is an unsigned 32-bit fixed-width integer.
75 Uint32 uint32
76 // Float32 is a 32-bit fixed-width floating point number.
77 Float32 float32
78
79 // Int64 is a signed 64-bit fixed-width integer.
80 Int64 int64
81 // Uint64 is an unsigned 64-bit fixed-width integer.
82 Uint64 uint64
83 // Float64 is a 64-bit fixed-width floating point number.
84 Float64 float64
85
86 // String is a length-prefixed string.
87 String string
88 // Bytes is a length-prefixed bytes.
89 Bytes []byte
90 // LengthPrefix is a length-prefixed message.
91 LengthPrefix Message
92
93 // Denormalized is a denormalized varint value, where a varint is encoded
94 // using more bytes than is strictly necessary. The number of extra bytes
95 // alone is sufficient to losslessly represent the denormalized varint.
96 //
97 // The value may be one of Tag, Bool, Varint, Svarint, or Uvarint,
98 // where the varint representation of each token is denormalized.
99 //
100 // Alternatively, the value may be one of String, Bytes, or LengthPrefix,
101 // where the varint representation of the length-prefix is denormalized.
102 Denormalized struct {
103 Count uint // number of extra bytes
104 Value Token
105 }
106
107 // Raw are bytes directly appended to output.
108 Raw []byte
109)
110
111type token interface {
112 isToken()
113}
114
115func (Message) isToken() {}
116func (Tag) isToken() {}
117func (Bool) isToken() {}
118func (Varint) isToken() {}
119func (Svarint) isToken() {}
120func (Uvarint) isToken() {}
121func (Int32) isToken() {}
122func (Uint32) isToken() {}
123func (Float32) isToken() {}
124func (Int64) isToken() {}
125func (Uint64) isToken() {}
126func (Float64) isToken() {}
127func (String) isToken() {}
128func (Bytes) isToken() {}
129func (LengthPrefix) isToken() {}
130func (Denormalized) isToken() {}
131func (Raw) isToken() {}
132
133// Size reports the size in bytes of the marshaled message.
134func (m Message) Size() int {
135 var n int
136 for _, v := range m {
137 switch v := v.(type) {
138 case Message:
139 n += v.Size()
140 case Tag:
141 n += wire.SizeTag(v.Number)
142 case Bool:
143 n += wire.SizeVarint(wire.EncodeBool(false))
144 case Varint:
145 n += wire.SizeVarint(uint64(v))
146 case Svarint:
147 n += wire.SizeVarint(wire.EncodeZigZag(int64(v)))
148 case Uvarint:
149 n += wire.SizeVarint(uint64(v))
150 case Int32, Uint32, Float32:
151 n += wire.SizeFixed32()
152 case Int64, Uint64, Float64:
153 n += wire.SizeFixed64()
154 case String:
155 n += wire.SizeBytes(len(v))
156 case Bytes:
157 n += wire.SizeBytes(len(v))
158 case LengthPrefix:
159 n += wire.SizeBytes(Message(v).Size())
160 case Denormalized:
161 n += int(v.Count) + Message{v.Value}.Size()
162 case Raw:
163 n += len(v)
164 default:
165 panic(fmt.Sprintf("unknown type: %T", v))
166 }
167 }
168 return n
169}
170
171// Message encodes an AST into the protobuf wire format.
172//
173// Example message definition:
174// message MyMessage {
175// string field1 = 1;
176// int64 field2 = 2;
177// repeated float32 field3 = 3;
178// }
179//
180// Example encoded message:
181// b := Message{
182// Tag{1, BytesType}, String("Hello, world!"),
183// Tag{2, VarintType}, Varint(-10),
184// Tag{3, BytesType}, LengthPrefix{
185// Float32(1.1), Float32(2.2), Float32(3.3),
186// },
187// }.Marshal()
188//
189// Resulting wire data:
190// 0x0000 0a 0d 48 65 6c 6c 6f 2c 20 77 6f 72 6c 64 21 10 |..Hello, world!.|
191// 0x0010 f6 ff ff ff ff ff ff ff ff 01 1a 0c cd cc 8c 3f |...............?|
192// 0x0020 cd cc 0c 40 33 33 53 40 |...@33S@|
193func (m Message) Marshal() []byte {
194 var out []byte
195 for _, v := range m {
196 switch v := v.(type) {
197 case Message:
198 out = append(out, v.Marshal()...)
199 case Tag:
200 out = wire.AppendTag(out, v.Number, v.Type)
201 case Bool:
202 out = wire.AppendVarint(out, wire.EncodeBool(bool(v)))
203 case Varint:
204 out = wire.AppendVarint(out, uint64(v))
205 case Svarint:
206 out = wire.AppendVarint(out, wire.EncodeZigZag(int64(v)))
207 case Uvarint:
208 out = wire.AppendVarint(out, uint64(v))
209 case Int32:
210 out = wire.AppendFixed32(out, uint32(v))
211 case Uint32:
212 out = wire.AppendFixed32(out, uint32(v))
213 case Float32:
214 out = wire.AppendFixed32(out, math.Float32bits(float32(v)))
215 case Int64:
216 out = wire.AppendFixed64(out, uint64(v))
217 case Uint64:
218 out = wire.AppendFixed64(out, uint64(v))
219 case Float64:
220 out = wire.AppendFixed64(out, math.Float64bits(float64(v)))
221 case String:
222 out = wire.AppendBytes(out, []byte(v))
223 case Bytes:
224 out = wire.AppendBytes(out, []byte(v))
225 case LengthPrefix:
226 out = wire.AppendBytes(out, Message(v).Marshal())
227 case Denormalized:
228 b := Message{v.Value}.Marshal()
229 _, n := wire.ConsumeVarint(b)
230 out = append(out, b[:n]...)
231 for i := uint(0); i < v.Count; i++ {
232 out[len(out)-1] |= 0x80 // set continuation bit on previous
233 out = append(out, 0)
234 }
235 out = append(out, b[n:]...)
236 case Raw:
237 return append(out, v...)
238 default:
239 panic(fmt.Sprintf("unknown type: %T", v))
240 }
241 }
242 return out
243}
244
245// Unmarshal parses the input protobuf wire data as a Message AST.
246// Any parsing error results in the remainder of the input being
247// concatenated to the message as a Raw type.
248//
249// Each tag (a tuple of the field number and wire type) encountered is
250// appended to the AST as a Tag.
251//
252// The contents of each wire type is mapped to the following Go types:
253// VarintType => Uvarint
254// Fixed32Type => Uint32
255// Fixed64Type => Uint64
256// BytesType => Bytes
257// GroupType => Message
258//
259// Since the wire format is not self-describing, this function cannot parse
260// sub-messages and will leave them as the Bytes type. Further manual parsing
261// can be performed as such:
262// var m, m1, m2 Message
263// m.Unmarshal(b)
264// m1.Unmarshal(m[3].(Bytes))
265// m[3] = LengthPrefix(m1)
266// m2.Unmarshal(m[3].(LengthPrefix)[1].(Bytes))
267// m[3].(LengthPrefix)[1] = LengthPrefix(m2)
268//
269// Unmarshal is useful for debugging the protobuf wire format.
270func (m *Message) Unmarshal(in []byte) {
271 m.UnmarshalDescriptor(in, nil)
272}
273
274// UnmarshalDescriptor parses the input protobuf wire data as a Message AST
275// using the provided message descriptor for more accurate parsing of fields.
276// It operates like Unmarshal, but may use a wider range of Go types to
277// represent the wire data.
278//
279// The contents of each wire type is mapped to one of the following Go types:
280// VarintType => Bool, Varint, Svarint, Uvarint
281// Fixed32Type => Int32, Uint32, Float32
282// Fixed64Type => Uint32, Uint64, Float64
283// BytesType => String, Bytes, LengthPrefix
284// GroupType => Message
285//
286// If the field is unknown, it uses the same mapping as Unmarshal.
287// Known sub-messages are parsed as a Message and packed repeated fields are
288// parsed as a LengthPrefix.
289func (m *Message) UnmarshalDescriptor(in []byte, desc protoreflect.MessageDescriptor) {
290 p := parser{in: in, out: *m}
291 p.parseMessage(desc, false)
292 *m = p.out
293}
294
295type parser struct {
296 in []byte
297 out []Token
298}
299
300func (p *parser) parseMessage(msgDesc protoreflect.MessageDescriptor, group bool) {
301 for len(p.in) > 0 {
302 v, n := wire.ConsumeVarint(p.in)
303 num, typ := wire.DecodeTag(v)
304 if n < 0 || num < 0 || v > math.MaxUint32 {
305 p.out, p.in = append(p.out, Raw(p.in)), nil
306 return
307 }
308 if typ == EndGroupType && group {
309 return // if inside a group, then stop
310 }
311 p.out, p.in = append(p.out, Tag{num, typ}), p.in[n:]
312 if m := n - wire.SizeVarint(v); m > 0 {
313 p.out[len(p.out)-1] = Denormalized{uint(m), p.out[len(p.out)-1]}
314 }
315
316 // If descriptor is available, use it for more accurate parsing.
317 var isPacked bool
318 var kind protoreflect.Kind
319 var subDesc protoreflect.MessageDescriptor
320 if msgDesc != nil && !msgDesc.IsPlaceholder() {
321 if fieldDesc := msgDesc.Fields().ByNumber(num); fieldDesc != nil {
322 isPacked = fieldDesc.IsPacked()
323 kind = fieldDesc.Kind()
324 switch kind {
325 case protoreflect.MessageKind, protoreflect.GroupKind:
326 subDesc = fieldDesc.MessageType()
327 if subDesc == nil || subDesc.IsPlaceholder() {
328 kind = 0
329 }
330 }
331 }
332 }
333
334 switch typ {
335 case VarintType:
336 p.parseVarint(kind)
337 case Fixed32Type:
338 p.parseFixed32(kind)
339 case Fixed64Type:
340 p.parseFixed64(kind)
341 case BytesType:
342 p.parseBytes(isPacked, kind, subDesc)
343 case StartGroupType:
344 p.parseGroup(subDesc)
345 case EndGroupType:
346 // Handled above.
347 default:
348 p.out, p.in = append(p.out, Raw(p.in)), nil
349 }
350 }
351}
352
353func (p *parser) parseVarint(kind protoreflect.Kind) {
354 v, n := wire.ConsumeVarint(p.in)
355 if n < 0 {
356 p.out, p.in = append(p.out, Raw(p.in)), nil
357 return
358 }
359 switch kind {
360 case protoreflect.BoolKind:
361 switch v {
362 case 0:
363 p.out, p.in = append(p.out, Bool(false)), p.in[n:]
364 case 1:
365 p.out, p.in = append(p.out, Bool(true)), p.in[n:]
366 default:
367 p.out, p.in = append(p.out, Uvarint(v)), p.in[n:]
368 }
369 case protoreflect.Int32Kind, protoreflect.Int64Kind:
370 p.out, p.in = append(p.out, Varint(v)), p.in[n:]
371 case protoreflect.Sint32Kind, protoreflect.Sint64Kind:
372 p.out, p.in = append(p.out, Svarint(wire.DecodeZigZag(v))), p.in[n:]
373 default:
374 p.out, p.in = append(p.out, Uvarint(v)), p.in[n:]
375 }
376 if m := n - wire.SizeVarint(v); m > 0 {
377 p.out[len(p.out)-1] = Denormalized{uint(m), p.out[len(p.out)-1]}
378 }
379}
380
381func (p *parser) parseFixed32(kind protoreflect.Kind) {
382 v, n := wire.ConsumeFixed32(p.in)
383 if n < 0 {
384 p.out, p.in = append(p.out, Raw(p.in)), nil
385 return
386 }
387 switch kind {
388 case protoreflect.FloatKind:
389 p.out, p.in = append(p.out, Float32(math.Float32frombits(v))), p.in[n:]
390 case protoreflect.Sfixed32Kind:
391 p.out, p.in = append(p.out, Int32(v)), p.in[n:]
392 default:
393 p.out, p.in = append(p.out, Uint32(v)), p.in[n:]
394 }
395}
396
397func (p *parser) parseFixed64(kind protoreflect.Kind) {
398 v, n := wire.ConsumeFixed64(p.in)
399 if n < 0 {
400 p.out, p.in = append(p.out, Raw(p.in)), nil
401 return
402 }
403 switch kind {
404 case protoreflect.DoubleKind:
405 p.out, p.in = append(p.out, Float64(math.Float64frombits(v))), p.in[n:]
406 case protoreflect.Sfixed64Kind:
407 p.out, p.in = append(p.out, Int64(v)), p.in[n:]
408 default:
409 p.out, p.in = append(p.out, Uint64(v)), p.in[n:]
410 }
411}
412
413func (p *parser) parseBytes(isPacked bool, kind protoreflect.Kind, desc protoreflect.MessageDescriptor) {
414 v, n := wire.ConsumeVarint(p.in)
415 if n < 0 {
416 p.out, p.in = append(p.out, Raw(p.in)), nil
417 return
418 }
419 p.out, p.in = append(p.out, Uvarint(v)), p.in[n:]
420 if m := n - wire.SizeVarint(v); m > 0 {
421 p.out[len(p.out)-1] = Denormalized{uint(m), p.out[len(p.out)-1]}
422 }
423 if v > uint64(len(p.in)) {
424 p.out, p.in = append(p.out, Raw(p.in)), nil
425 return
426 }
427 p.out = p.out[:len(p.out)-1] // subsequent tokens contain prefix-length
428
429 if isPacked {
430 p.parsePacked(int(v), kind)
431 } else {
432 switch kind {
433 case protoreflect.MessageKind:
434 p2 := parser{in: p.in[:v]}
435 p2.parseMessage(desc, false)
436 p.out, p.in = append(p.out, LengthPrefix(p2.out)), p.in[v:]
437 case protoreflect.StringKind:
438 p.out, p.in = append(p.out, String(p.in[:v])), p.in[v:]
439 default:
440 p.out, p.in = append(p.out, Bytes(p.in[:v])), p.in[v:]
441 }
442 }
443 if m := n - wire.SizeVarint(v); m > 0 {
444 p.out[len(p.out)-1] = Denormalized{uint(m), p.out[len(p.out)-1]}
445 }
446}
447
448func (p *parser) parsePacked(n int, kind protoreflect.Kind) {
449 p2 := parser{in: p.in[:n]}
450 for len(p2.in) > 0 {
451 switch kind {
452 case protoreflect.BoolKind, protoreflect.EnumKind,
453 protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Uint32Kind,
454 protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Uint64Kind:
455 p2.parseVarint(kind)
456 case protoreflect.Fixed32Kind, protoreflect.Sfixed32Kind, protoreflect.FloatKind:
457 p2.parseFixed32(kind)
458 case protoreflect.Fixed64Kind, protoreflect.Sfixed64Kind, protoreflect.DoubleKind:
459 p2.parseFixed64(kind)
460 default:
461 panic(fmt.Sprintf("invalid packed kind: %v", kind))
462 }
463 }
464 p.out, p.in = append(p.out, LengthPrefix(p2.out)), p.in[n:]
465}
466
467func (p *parser) parseGroup(desc protoreflect.MessageDescriptor) {
468 p2 := parser{in: p.in}
469 p2.parseMessage(desc, true)
470 if len(p2.out) > 0 {
471 p.out = append(p.out, Message(p2.out))
472 }
473 p.in = p2.in
474
475 // Append the trailing end group.
476 v, n := wire.ConsumeVarint(p.in)
477 if num, typ := wire.DecodeTag(v); typ == EndGroupType {
478 p.out, p.in = append(p.out, Tag{num, typ}), p.in[n:]
479 if m := n - wire.SizeVarint(v); m > 0 {
480 p.out[len(p.out)-1] = Denormalized{uint(m), p.out[len(p.out)-1]}
481 }
482 }
483}
484
485// Format implements a custom formatter to visualize the Message AST.
486// Using "%#v" formats the Message in Go source code.
487func (m Message) Format(s fmt.State, r rune) {
488 switch r {
489 case 'x':
490 io.WriteString(s, fmt.Sprintf("%x", m.Marshal()))
491 case 'X':
492 io.WriteString(s, fmt.Sprintf("%X", m.Marshal()))
493 case 'v':
494 switch {
495 case s.Flag('#'):
496 io.WriteString(s, m.format(true, true))
497 case s.Flag('+'):
498 io.WriteString(s, m.format(false, true))
499 default:
500 io.WriteString(s, m.format(false, false))
501 }
502 default:
503 panic("invalid verb: " + string(r))
504 }
505}
506
507// format formats the message.
508// If source is enabled, this emits valid Go source.
509// If multi is enabled, the output may span multiple lines.
510func (m Message) format(source, multi bool) string {
511 var ss []string
512 var prefix, nextPrefix string
513 for _, v := range m {
514 // Ensure certain tokens have preceding or succeeding newlines.
515 prefix, nextPrefix = nextPrefix, " "
516 if multi {
517 switch v := v.(type) {
518 case Tag: // only has preceding newline
519 prefix = "\n"
520 case Denormalized: // only has preceding newline
521 if _, ok := v.Value.(Tag); ok {
522 prefix = "\n"
523 }
524 case Message, Raw: // has preceding and succeeding newlines
525 prefix, nextPrefix = "\n", "\n"
526 }
527 }
528
529 s := formatToken(v, source, multi)
530 ss = append(ss, prefix+s+",")
531 }
532
533 var s string
534 if len(ss) > 0 {
535 s = strings.TrimSpace(strings.Join(ss, ""))
536 if multi {
537 s = "\n\t" + strings.Join(strings.Split(s, "\n"), "\n\t") + "\n"
538 } else {
539 s = strings.TrimSuffix(s, ",")
540 }
541 }
542 s = fmt.Sprintf("%T{%s}", m, s)
543 if !source {
544 s = trimPackage(s)
545 }
546 return s
547}
548
549// formatToken formats a single token.
550func formatToken(t Token, source, multi bool) (s string) {
551 switch v := t.(type) {
552 case Message:
553 s = v.format(source, multi)
554 case LengthPrefix:
555 s = formatPacked(v, source, multi)
556 if s == "" {
557 ms := Message(v).format(source, multi)
558 s = fmt.Sprintf("%T(%s)", v, ms)
559 }
560 case Tag:
561 s = fmt.Sprintf("%T{%d, %s}", v, v.Number, formatType(v.Type, source))
562 case Bool, Varint, Svarint, Uvarint, Int32, Uint32, Float32, Int64, Uint64, Float64:
563 if source {
564 // Print floats in a way that preserves exact precision.
565 if f, _ := v.(Float32); math.IsNaN(float64(f)) || math.IsInf(float64(f), 0) {
566 switch {
567 case f > 0:
568 s = fmt.Sprintf("%T(math.Inf(+1))", v)
569 case f < 0:
570 s = fmt.Sprintf("%T(math.Inf(-1))", v)
571 case math.Float32bits(float32(math.NaN())) == math.Float32bits(float32(f)):
572 s = fmt.Sprintf("%T(math.NaN())", v)
573 default:
574 s = fmt.Sprintf("%T(math.Float32frombits(0x%08x))", v, math.Float32bits(float32(f)))
575 }
576 break
577 }
578 if f, _ := v.(Float64); math.IsNaN(float64(f)) || math.IsInf(float64(f), 0) {
579 switch {
580 case f > 0:
581 s = fmt.Sprintf("%T(math.Inf(+1))", v)
582 case f < 0:
583 s = fmt.Sprintf("%T(math.Inf(-1))", v)
584 case math.Float64bits(float64(math.NaN())) == math.Float64bits(float64(f)):
585 s = fmt.Sprintf("%T(math.NaN())", v)
586 default:
587 s = fmt.Sprintf("%T(math.Float64frombits(0x%08x))", v, math.Float64bits(float64(f)))
588 }
589 break
590 }
591 }
592 s = fmt.Sprintf("%T(%v)", v, v)
593 case String, Bytes, Raw:
594 s = fmt.Sprintf("%s", v)
595 s = fmt.Sprintf("%T(%s)", v, formatString(s))
596 case Denormalized:
597 s = fmt.Sprintf("%T{+%d, %v}", v, v.Count, formatToken(v.Value, source, multi))
598 default:
599 panic(fmt.Sprintf("unknown type: %T", v))
600 }
601 if !source {
602 s = trimPackage(s)
603 }
604 return s
605}
606
607// formatPacked returns a non-empty string if LengthPrefix looks like a packed
608// repeated field of primitives.
609func formatPacked(v LengthPrefix, source, multi bool) string {
610 var ss []string
611 for _, v := range v {
612 switch v.(type) {
613 case Bool, Varint, Svarint, Uvarint, Int32, Uint32, Float32, Int64, Uint64, Float64, Denormalized, Raw:
614 if v, ok := v.(Denormalized); ok {
615 switch v.Value.(type) {
616 case Bool, Varint, Svarint, Uvarint:
617 default:
618 return ""
619 }
620 }
621 ss = append(ss, formatToken(v, source, multi))
622 default:
623 return ""
624 }
625 }
626 s := fmt.Sprintf("%T{%s}", v, strings.Join(ss, ", "))
627 if !source {
628 s = trimPackage(s)
629 }
630 return s
631}
632
633// formatType returns the name for Type.
634func formatType(t Type, source bool) (s string) {
635 switch t {
636 case VarintType:
637 s = pkg + ".VarintType"
638 case Fixed32Type:
639 s = pkg + ".Fixed32Type"
640 case Fixed64Type:
641 s = pkg + ".Fixed64Type"
642 case BytesType:
643 s = pkg + ".BytesType"
644 case StartGroupType:
645 s = pkg + ".StartGroupType"
646 case EndGroupType:
647 s = pkg + ".EndGroupType"
648 default:
649 s = fmt.Sprintf("Type(%d)", t)
650 }
651 if !source {
652 s = strings.TrimSuffix(trimPackage(s), "Type")
653 }
654 return s
655}
656
657// formatString returns a quoted string for s.
658func formatString(s string) string {
659 // Use quoted string if it the same length as a raw string literal.
660 // Otherwise, attempt to use the raw string form.
661 qs := strconv.Quote(s)
662 if len(qs) == 1+len(s)+1 {
663 return qs
664 }
665
666 // Disallow newlines to ensure output is a single line.
667 // Disallow non-printable runes for readability purposes.
668 rawInvalid := func(r rune) bool {
669 return r == '`' || r == '\n' || r == utf8.RuneError || !unicode.IsPrint(r)
670 }
671 if strings.IndexFunc(s, rawInvalid) < 0 {
672 return "`" + s + "`"
673 }
674 return qs
675}
676
677var pkg = path.Base(reflect.TypeOf(Tag{}).PkgPath())
678
679func trimPackage(s string) string {
680 return strings.TrimPrefix(strings.TrimPrefix(s, pkg), ".")
681}