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