encoder.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. # Protocol Buffers - Google's data interchange format
  2. # Copyright 2008 Google Inc.
  3. # http://code.google.com/p/protobuf/
  4. #
  5. # Licensed under the Apache License, Version 2.0 (the "License");
  6. # you may not use this file except in compliance with the License.
  7. # You may obtain a copy of the License at
  8. #
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing, software
  12. # distributed under the License is distributed on an "AS IS" BASIS,
  13. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. # See the License for the specific language governing permissions and
  15. # limitations under the License.
  16. """Class for encoding protocol message primitives.
  17. Contains the logic for encoding every logical protocol field type
  18. into one of the 5 physical wire types.
  19. """
  20. __author__ = 'robinson@google.com (Will Robinson)'
  21. import struct
  22. from google.protobuf import message
  23. from google.protobuf.internal import wire_format
  24. from google.protobuf.internal import output_stream
  25. # Note that much of this code is ported from //net/proto/ProtocolBuffer, and
  26. # that the interface is strongly inspired by WireFormat from the C++ proto2
  27. # implementation.
  28. class Encoder(object):
  29. """Encodes logical protocol buffer fields to the wire format."""
  30. def __init__(self):
  31. self._stream = output_stream.OutputStream()
  32. def ToString(self):
  33. """Returns all values encoded in this object as a string."""
  34. return self._stream.ToString()
  35. # All the Append*() methods below first append a tag+type pair to the buffer
  36. # before appending the specified value.
  37. def AppendInt32(self, field_number, value):
  38. """Appends a 32-bit integer to our buffer, varint-encoded."""
  39. self._AppendTag(field_number, wire_format.WIRETYPE_VARINT)
  40. self._stream.AppendVarint32(value)
  41. def AppendInt64(self, field_number, value):
  42. """Appends a 64-bit integer to our buffer, varint-encoded."""
  43. self._AppendTag(field_number, wire_format.WIRETYPE_VARINT)
  44. self._stream.AppendVarint64(value)
  45. def AppendUInt32(self, field_number, unsigned_value):
  46. """Appends an unsigned 32-bit integer to our buffer, varint-encoded."""
  47. self._AppendTag(field_number, wire_format.WIRETYPE_VARINT)
  48. self._stream.AppendVarUInt32(unsigned_value)
  49. def AppendUInt64(self, field_number, unsigned_value):
  50. """Appends an unsigned 64-bit integer to our buffer, varint-encoded."""
  51. self._AppendTag(field_number, wire_format.WIRETYPE_VARINT)
  52. self._stream.AppendVarUInt64(unsigned_value)
  53. def AppendSInt32(self, field_number, value):
  54. """Appends a 32-bit integer to our buffer, zigzag-encoded and then
  55. varint-encoded.
  56. """
  57. self._AppendTag(field_number, wire_format.WIRETYPE_VARINT)
  58. zigzag_value = wire_format.ZigZagEncode(value)
  59. self._stream.AppendVarUInt32(zigzag_value)
  60. def AppendSInt64(self, field_number, value):
  61. """Appends a 64-bit integer to our buffer, zigzag-encoded and then
  62. varint-encoded.
  63. """
  64. self._AppendTag(field_number, wire_format.WIRETYPE_VARINT)
  65. zigzag_value = wire_format.ZigZagEncode(value)
  66. self._stream.AppendVarUInt64(zigzag_value)
  67. def AppendFixed32(self, field_number, unsigned_value):
  68. """Appends an unsigned 32-bit integer to our buffer, in little-endian
  69. byte-order.
  70. """
  71. self._AppendTag(field_number, wire_format.WIRETYPE_FIXED32)
  72. self._stream.AppendLittleEndian32(unsigned_value)
  73. def AppendFixed64(self, field_number, unsigned_value):
  74. """Appends an unsigned 64-bit integer to our buffer, in little-endian
  75. byte-order.
  76. """
  77. self._AppendTag(field_number, wire_format.WIRETYPE_FIXED64)
  78. self._stream.AppendLittleEndian64(unsigned_value)
  79. def AppendSFixed32(self, field_number, value):
  80. """Appends a signed 32-bit integer to our buffer, in little-endian
  81. byte-order.
  82. """
  83. sign = (value & 0x80000000) and -1 or 0
  84. if value >> 32 != sign:
  85. raise message.EncodeError('SFixed32 out of range: %d' % value)
  86. self._AppendTag(field_number, wire_format.WIRETYPE_FIXED32)
  87. self._stream.AppendLittleEndian32(value & 0xffffffff)
  88. def AppendSFixed64(self, field_number, value):
  89. """Appends a signed 64-bit integer to our buffer, in little-endian
  90. byte-order.
  91. """
  92. sign = (value & 0x8000000000000000) and -1 or 0
  93. if value >> 64 != sign:
  94. raise message.EncodeError('SFixed64 out of range: %d' % value)
  95. self._AppendTag(field_number, wire_format.WIRETYPE_FIXED64)
  96. self._stream.AppendLittleEndian64(value & 0xffffffffffffffff)
  97. def AppendFloat(self, field_number, value):
  98. """Appends a floating-point number to our buffer."""
  99. self._AppendTag(field_number, wire_format.WIRETYPE_FIXED32)
  100. self._stream.AppendRawBytes(struct.pack('f', value))
  101. def AppendDouble(self, field_number, value):
  102. """Appends a double-precision floating-point number to our buffer."""
  103. self._AppendTag(field_number, wire_format.WIRETYPE_FIXED64)
  104. self._stream.AppendRawBytes(struct.pack('d', value))
  105. def AppendBool(self, field_number, value):
  106. """Appends a boolean to our buffer."""
  107. self.AppendInt32(field_number, value)
  108. def AppendEnum(self, field_number, value):
  109. """Appends an enum value to our buffer."""
  110. self.AppendInt32(field_number, value)
  111. def AppendString(self, field_number, value):
  112. """Appends a length-prefixed string to our buffer, with the
  113. length varint-encoded.
  114. """
  115. self._AppendTag(field_number, wire_format.WIRETYPE_LENGTH_DELIMITED)
  116. self._stream.AppendVarUInt32(len(value))
  117. self._stream.AppendRawBytes(value)
  118. def AppendBytes(self, field_number, value):
  119. """Appends a length-prefixed sequence of bytes to our buffer, with the
  120. length varint-encoded.
  121. """
  122. self.AppendString(field_number, value)
  123. # TODO(robinson): For AppendGroup() and AppendMessage(), we'd really like to
  124. # avoid the extra string copy here. We can do so if we widen the Message
  125. # interface to be able to serialize to a stream in addition to a string. The
  126. # challenge when thinking ahead to the Python/C API implementation of Message
  127. # is finding a stream-like Python thing to which we can write raw bytes
  128. # from C. I'm not sure such a thing exists(?). (array.array is pretty much
  129. # what we want, but it's not directly exposed in the Python/C API).
  130. def AppendGroup(self, field_number, group):
  131. """Appends a group to our buffer.
  132. """
  133. self._AppendTag(field_number, wire_format.WIRETYPE_START_GROUP)
  134. self._stream.AppendRawBytes(group.SerializeToString())
  135. self._AppendTag(field_number, wire_format.WIRETYPE_END_GROUP)
  136. def AppendMessage(self, field_number, msg):
  137. """Appends a nested message to our buffer.
  138. """
  139. self._AppendTag(field_number, wire_format.WIRETYPE_LENGTH_DELIMITED)
  140. self._stream.AppendVarUInt32(msg.ByteSize())
  141. self._stream.AppendRawBytes(msg.SerializeToString())
  142. def AppendMessageSetItem(self, field_number, msg):
  143. """Appends an item using the message set wire format.
  144. The message set message looks like this:
  145. message MessageSet {
  146. repeated group Item = 1 {
  147. required int32 type_id = 2;
  148. required string message = 3;
  149. }
  150. }
  151. """
  152. self._AppendTag(1, wire_format.WIRETYPE_START_GROUP)
  153. self.AppendInt32(2, field_number)
  154. self.AppendMessage(3, msg)
  155. self._AppendTag(1, wire_format.WIRETYPE_END_GROUP)
  156. def _AppendTag(self, field_number, wire_type):
  157. """Appends a tag containing field number and wire type information."""
  158. self._stream.AppendVarUInt32(wire_format.PackTag(field_number, wire_type))