encode.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  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. package text
  5. import (
  6. "regexp"
  7. "strings"
  8. "google.golang.org/protobuf/internal/detrand"
  9. "google.golang.org/protobuf/internal/errors"
  10. )
  11. // Marshal serializes v as the proto text format, where v must be a Message.
  12. // In the proto text format, the top-level value is always a message where the
  13. // delimiters are elided.
  14. //
  15. // If indent is a non-empty string, it causes every entry in a List or Message
  16. // to be preceded by the indent and trailed by a newline.
  17. //
  18. // If delims is not the zero value, it controls the delimiter characters used
  19. // for messages (e.g., "{}" vs "<>").
  20. //
  21. // If outputASCII is true, strings will be serialized in such a way that
  22. // multi-byte UTF-8 sequences are escaped. This property ensures that the
  23. // overall output is ASCII (as opposed to UTF-8).
  24. func Marshal(v Value, indent string, delims [2]byte, outputASCII bool) ([]byte, error) {
  25. p := encoder{}
  26. if len(indent) > 0 {
  27. if strings.Trim(indent, " \t") != "" {
  28. return nil, errors.New("indent may only be composed of space and tab characters")
  29. }
  30. p.indent = indent
  31. p.newline = "\n"
  32. }
  33. switch delims {
  34. case [2]byte{0, 0}:
  35. p.delims = [2]byte{'{', '}'}
  36. case [2]byte{'{', '}'}, [2]byte{'<', '>'}:
  37. p.delims = delims
  38. default:
  39. return nil, errors.New("delimiters may only be \"{}\" or \"<>\"")
  40. }
  41. p.outputASCII = outputASCII
  42. err := p.marshalMessage(v, false)
  43. if err != nil {
  44. return nil, err
  45. }
  46. return p.out, nil
  47. }
  48. type encoder struct {
  49. out []byte
  50. indent string
  51. indents []byte
  52. newline string // set to "\n" if len(indent) > 0
  53. delims [2]byte
  54. outputASCII bool
  55. }
  56. func (p *encoder) marshalList(v Value) error {
  57. if v.Type() != List {
  58. return errors.New("invalid type %v, expected list", v.Type())
  59. }
  60. elems := v.List()
  61. p.out = append(p.out, '[')
  62. p.indents = append(p.indents, p.indent...)
  63. if len(elems) > 0 {
  64. p.out = append(p.out, p.newline...)
  65. }
  66. for i, elem := range elems {
  67. p.out = append(p.out, p.indents...)
  68. if err := p.marshalValue(elem); err != nil {
  69. return err
  70. }
  71. if i < len(elems)-1 {
  72. p.out = append(p.out, ',')
  73. }
  74. p.out = append(p.out, p.newline...)
  75. }
  76. p.indents = p.indents[:len(p.indents)-len(p.indent)]
  77. if len(elems) > 0 {
  78. p.out = append(p.out, p.indents...)
  79. }
  80. p.out = append(p.out, ']')
  81. return nil
  82. }
  83. func (p *encoder) marshalMessage(v Value, emitDelims bool) error {
  84. if v.Type() != Message {
  85. return errors.New("invalid type %v, expected message", v.Type())
  86. }
  87. items := v.Message()
  88. if emitDelims {
  89. p.out = append(p.out, p.delims[0])
  90. p.indents = append(p.indents, p.indent...)
  91. if len(items) > 0 {
  92. p.out = append(p.out, p.newline...)
  93. }
  94. }
  95. for i, item := range items {
  96. p.out = append(p.out, p.indents...)
  97. if err := p.marshalKey(item[0]); err != nil {
  98. return err
  99. }
  100. p.out = append(p.out, ':')
  101. if len(p.indent) > 0 {
  102. p.out = append(p.out, ' ')
  103. // For multi-line output, add a random extra space after key:
  104. // to make output unstable.
  105. if detrand.Bool() {
  106. p.out = append(p.out, ' ')
  107. }
  108. }
  109. if err := p.marshalValue(item[1]); err != nil {
  110. return err
  111. }
  112. if i < len(items)-1 && len(p.indent) == 0 {
  113. p.out = append(p.out, ' ')
  114. // For single-line output, add a random extra space after a field
  115. // to make output unstable.
  116. if detrand.Bool() {
  117. p.out = append(p.out, ' ')
  118. }
  119. }
  120. p.out = append(p.out, p.newline...)
  121. }
  122. if emitDelims {
  123. p.indents = p.indents[:len(p.indents)-len(p.indent)]
  124. if len(items) > 0 {
  125. p.out = append(p.out, p.indents...)
  126. }
  127. p.out = append(p.out, p.delims[1])
  128. }
  129. return nil
  130. }
  131. // This expression is more liberal than ConsumeAnyTypeUrl in C++.
  132. // However, the C++ parser does not handle many legal URL strings.
  133. // The Go implementation is more liberal to be backwards compatible with
  134. // the historical Go implementation which was overly liberal (and buggy).
  135. var urlRegexp = regexp.MustCompile(`^[-_a-zA-Z0-9]+([./][-_a-zA-Z0-9]+)*`)
  136. func (p *encoder) marshalKey(v Value) error {
  137. switch v.Type() {
  138. case String:
  139. var err error
  140. p.out = append(p.out, '[')
  141. if len(urlRegexp.FindString(v.str)) == len(v.str) {
  142. p.out = append(p.out, v.str...)
  143. } else {
  144. err = p.marshalString(v)
  145. }
  146. p.out = append(p.out, ']')
  147. return err
  148. case Uint:
  149. return p.marshalNumber(v)
  150. case Name:
  151. s, _ := v.Name()
  152. p.out = append(p.out, s...)
  153. return nil
  154. default:
  155. return errors.New("invalid type %v to encode key", v.Type())
  156. }
  157. }
  158. func (p *encoder) marshalValue(v Value) error {
  159. switch v.Type() {
  160. case Bool, Int, Uint, Float32, Float64:
  161. return p.marshalNumber(v)
  162. case String:
  163. return p.marshalString(v)
  164. case List:
  165. return p.marshalList(v)
  166. case Message:
  167. return p.marshalMessage(v, true)
  168. case Name:
  169. s, _ := v.Name()
  170. p.out = append(p.out, s...)
  171. return nil
  172. default:
  173. return errors.New("invalid type %v to encode value", v.Type())
  174. }
  175. }