mirror of
https://gerrit.googlesource.com/git-repo
synced 2024-12-23 07:16:21 +00:00
207 lines
8.4 KiB
Python
207 lines
8.4 KiB
Python
|
# Protocol Buffers - Google's data interchange format
|
||
|
# Copyright 2008 Google Inc. All rights reserved.
|
||
|
# http://code.google.com/p/protobuf/
|
||
|
#
|
||
|
# Redistribution and use in source and binary forms, with or without
|
||
|
# modification, are permitted provided that the following conditions are
|
||
|
# met:
|
||
|
#
|
||
|
# * Redistributions of source code must retain the above copyright
|
||
|
# notice, this list of conditions and the following disclaimer.
|
||
|
# * Redistributions in binary form must reproduce the above
|
||
|
# copyright notice, this list of conditions and the following disclaimer
|
||
|
# in the documentation and/or other materials provided with the
|
||
|
# distribution.
|
||
|
# * Neither the name of Google Inc. nor the names of its
|
||
|
# contributors may be used to endorse or promote products derived from
|
||
|
# this software without specific prior written permission.
|
||
|
#
|
||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||
|
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||
|
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||
|
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||
|
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||
|
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||
|
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||
|
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||
|
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
|
|
||
|
"""Class for encoding protocol message primitives.
|
||
|
|
||
|
Contains the logic for encoding every logical protocol field type
|
||
|
into one of the 5 physical wire types.
|
||
|
"""
|
||
|
|
||
|
__author__ = 'robinson@google.com (Will Robinson)'
|
||
|
|
||
|
import struct
|
||
|
from froofle.protobuf import message
|
||
|
from froofle.protobuf.internal import wire_format
|
||
|
from froofle.protobuf.internal import output_stream
|
||
|
|
||
|
|
||
|
# Note that much of this code is ported from //net/proto/ProtocolBuffer, and
|
||
|
# that the interface is strongly inspired by WireFormat from the C++ proto2
|
||
|
# implementation.
|
||
|
|
||
|
|
||
|
class Encoder(object):
|
||
|
|
||
|
"""Encodes logical protocol buffer fields to the wire format."""
|
||
|
|
||
|
def __init__(self):
|
||
|
self._stream = output_stream.OutputStream()
|
||
|
|
||
|
def ToString(self):
|
||
|
"""Returns all values encoded in this object as a string."""
|
||
|
return self._stream.ToString()
|
||
|
|
||
|
# All the Append*() methods below first append a tag+type pair to the buffer
|
||
|
# before appending the specified value.
|
||
|
|
||
|
def AppendInt32(self, field_number, value):
|
||
|
"""Appends a 32-bit integer to our buffer, varint-encoded."""
|
||
|
self._AppendTag(field_number, wire_format.WIRETYPE_VARINT)
|
||
|
self._stream.AppendVarint32(value)
|
||
|
|
||
|
def AppendInt64(self, field_number, value):
|
||
|
"""Appends a 64-bit integer to our buffer, varint-encoded."""
|
||
|
self._AppendTag(field_number, wire_format.WIRETYPE_VARINT)
|
||
|
self._stream.AppendVarint64(value)
|
||
|
|
||
|
def AppendUInt32(self, field_number, unsigned_value):
|
||
|
"""Appends an unsigned 32-bit integer to our buffer, varint-encoded."""
|
||
|
self._AppendTag(field_number, wire_format.WIRETYPE_VARINT)
|
||
|
self._stream.AppendVarUInt32(unsigned_value)
|
||
|
|
||
|
def AppendUInt64(self, field_number, unsigned_value):
|
||
|
"""Appends an unsigned 64-bit integer to our buffer, varint-encoded."""
|
||
|
self._AppendTag(field_number, wire_format.WIRETYPE_VARINT)
|
||
|
self._stream.AppendVarUInt64(unsigned_value)
|
||
|
|
||
|
def AppendSInt32(self, field_number, value):
|
||
|
"""Appends a 32-bit integer to our buffer, zigzag-encoded and then
|
||
|
varint-encoded.
|
||
|
"""
|
||
|
self._AppendTag(field_number, wire_format.WIRETYPE_VARINT)
|
||
|
zigzag_value = wire_format.ZigZagEncode(value)
|
||
|
self._stream.AppendVarUInt32(zigzag_value)
|
||
|
|
||
|
def AppendSInt64(self, field_number, value):
|
||
|
"""Appends a 64-bit integer to our buffer, zigzag-encoded and then
|
||
|
varint-encoded.
|
||
|
"""
|
||
|
self._AppendTag(field_number, wire_format.WIRETYPE_VARINT)
|
||
|
zigzag_value = wire_format.ZigZagEncode(value)
|
||
|
self._stream.AppendVarUInt64(zigzag_value)
|
||
|
|
||
|
def AppendFixed32(self, field_number, unsigned_value):
|
||
|
"""Appends an unsigned 32-bit integer to our buffer, in little-endian
|
||
|
byte-order.
|
||
|
"""
|
||
|
self._AppendTag(field_number, wire_format.WIRETYPE_FIXED32)
|
||
|
self._stream.AppendLittleEndian32(unsigned_value)
|
||
|
|
||
|
def AppendFixed64(self, field_number, unsigned_value):
|
||
|
"""Appends an unsigned 64-bit integer to our buffer, in little-endian
|
||
|
byte-order.
|
||
|
"""
|
||
|
self._AppendTag(field_number, wire_format.WIRETYPE_FIXED64)
|
||
|
self._stream.AppendLittleEndian64(unsigned_value)
|
||
|
|
||
|
def AppendSFixed32(self, field_number, value):
|
||
|
"""Appends a signed 32-bit integer to our buffer, in little-endian
|
||
|
byte-order.
|
||
|
"""
|
||
|
sign = (value & 0x80000000) and -1 or 0
|
||
|
if value >> 32 != sign:
|
||
|
raise message.EncodeError('SFixed32 out of range: %d' % value)
|
||
|
self._AppendTag(field_number, wire_format.WIRETYPE_FIXED32)
|
||
|
self._stream.AppendLittleEndian32(value & 0xffffffff)
|
||
|
|
||
|
def AppendSFixed64(self, field_number, value):
|
||
|
"""Appends a signed 64-bit integer to our buffer, in little-endian
|
||
|
byte-order.
|
||
|
"""
|
||
|
sign = (value & 0x8000000000000000) and -1 or 0
|
||
|
if value >> 64 != sign:
|
||
|
raise message.EncodeError('SFixed64 out of range: %d' % value)
|
||
|
self._AppendTag(field_number, wire_format.WIRETYPE_FIXED64)
|
||
|
self._stream.AppendLittleEndian64(value & 0xffffffffffffffff)
|
||
|
|
||
|
def AppendFloat(self, field_number, value):
|
||
|
"""Appends a floating-point number to our buffer."""
|
||
|
self._AppendTag(field_number, wire_format.WIRETYPE_FIXED32)
|
||
|
self._stream.AppendRawBytes(struct.pack('f', value))
|
||
|
|
||
|
def AppendDouble(self, field_number, value):
|
||
|
"""Appends a double-precision floating-point number to our buffer."""
|
||
|
self._AppendTag(field_number, wire_format.WIRETYPE_FIXED64)
|
||
|
self._stream.AppendRawBytes(struct.pack('d', value))
|
||
|
|
||
|
def AppendBool(self, field_number, value):
|
||
|
"""Appends a boolean to our buffer."""
|
||
|
self.AppendInt32(field_number, value)
|
||
|
|
||
|
def AppendEnum(self, field_number, value):
|
||
|
"""Appends an enum value to our buffer."""
|
||
|
self.AppendInt32(field_number, value)
|
||
|
|
||
|
def AppendString(self, field_number, value):
|
||
|
"""Appends a length-prefixed unicode string, encoded as UTF-8 to our buffer,
|
||
|
with the length varint-encoded.
|
||
|
"""
|
||
|
self.AppendBytes(field_number, value.encode('utf-8'))
|
||
|
|
||
|
def AppendBytes(self, field_number, value):
|
||
|
"""Appends a length-prefixed sequence of bytes to our buffer, with the
|
||
|
length varint-encoded.
|
||
|
"""
|
||
|
self._AppendTag(field_number, wire_format.WIRETYPE_LENGTH_DELIMITED)
|
||
|
self._stream.AppendVarUInt32(len(value))
|
||
|
self._stream.AppendRawBytes(value)
|
||
|
|
||
|
# TODO(robinson): For AppendGroup() and AppendMessage(), we'd really like to
|
||
|
# avoid the extra string copy here. We can do so if we widen the Message
|
||
|
# interface to be able to serialize to a stream in addition to a string. The
|
||
|
# challenge when thinking ahead to the Python/C API implementation of Message
|
||
|
# is finding a stream-like Python thing to which we can write raw bytes
|
||
|
# from C. I'm not sure such a thing exists(?). (array.array is pretty much
|
||
|
# what we want, but it's not directly exposed in the Python/C API).
|
||
|
|
||
|
def AppendGroup(self, field_number, group):
|
||
|
"""Appends a group to our buffer.
|
||
|
"""
|
||
|
self._AppendTag(field_number, wire_format.WIRETYPE_START_GROUP)
|
||
|
self._stream.AppendRawBytes(group.SerializeToString())
|
||
|
self._AppendTag(field_number, wire_format.WIRETYPE_END_GROUP)
|
||
|
|
||
|
def AppendMessage(self, field_number, msg):
|
||
|
"""Appends a nested message to our buffer.
|
||
|
"""
|
||
|
self._AppendTag(field_number, wire_format.WIRETYPE_LENGTH_DELIMITED)
|
||
|
self._stream.AppendVarUInt32(msg.ByteSize())
|
||
|
self._stream.AppendRawBytes(msg.SerializeToString())
|
||
|
|
||
|
def AppendMessageSetItem(self, field_number, msg):
|
||
|
"""Appends an item using the message set wire format.
|
||
|
|
||
|
The message set message looks like this:
|
||
|
message MessageSet {
|
||
|
repeated group Item = 1 {
|
||
|
required int32 type_id = 2;
|
||
|
required string message = 3;
|
||
|
}
|
||
|
}
|
||
|
"""
|
||
|
self._AppendTag(1, wire_format.WIRETYPE_START_GROUP)
|
||
|
self.AppendInt32(2, field_number)
|
||
|
self.AppendMessage(3, msg)
|
||
|
self._AppendTag(1, wire_format.WIRETYPE_END_GROUP)
|
||
|
|
||
|
def _AppendTag(self, field_number, wire_type):
|
||
|
"""Appends a tag containing field number and wire type information."""
|
||
|
self._stream.AppendVarUInt32(wire_format.PackTag(field_number, wire_type))
|