1
# Protocol Buffers - Google's data interchange format
2
# Copyright 2008 Google Inc.
3
# http://code.google.com/p/protobuf/
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
9
# http://www.apache.org/licenses/LICENSE-2.0
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.
17
"""Class for decoding protocol buffer primitives.
19
Contains the logic for decoding every logical protocol field type
20
from one of the 5 physical wire types.
23
__author__ = 'robinson@google.com (Will Robinson)'
26
from google.protobuf import message
27
from google.protobuf.internal import input_stream
28
from google.protobuf.internal import wire_format
32
# Note that much of this code is ported from //net/proto/ProtocolBuffer, and
33
# that the interface is strongly inspired by WireFormat from the C++ proto2
37
class Decoder(object):
39
"""Decodes logical protocol buffer fields from the wire."""
41
def __init__(self, s):
42
"""Initializes the decoder to read from s.
45
s: An immutable sequence of bytes, which must be accessible
46
via the Python buffer() primitive (i.e., buffer(s)).
48
self._stream = input_stream.InputStream(s)
50
def EndOfStream(self):
51
"""Returns true iff we've reached the end of the bytes we're reading."""
52
return self._stream.EndOfStream()
55
"""Returns the 0-indexed position in |s|."""
56
return self._stream.Position()
58
def ReadFieldNumberAndWireType(self):
59
"""Reads a tag from the wire. Returns a (field_number, wire_type) pair."""
60
tag_and_type = self.ReadUInt32()
61
return wire_format.UnpackTag(tag_and_type)
63
def SkipBytes(self, bytes):
64
"""Skips the specified number of bytes on the wire."""
65
self._stream.SkipBytes(bytes)
67
# Note that the Read*() methods below are not exactly symmetrical with the
68
# corresponding Encoder.Append*() methods. Those Encoder methods first
69
# encode a tag, but the Read*() methods below assume that the tag has already
70
# been read, and that the client wishes to read a field of the specified type
71
# starting at the current position.
74
"""Reads and returns a signed, varint-encoded, 32-bit integer."""
75
return self._stream.ReadVarint32()
78
"""Reads and returns a signed, varint-encoded, 64-bit integer."""
79
return self._stream.ReadVarint64()
82
"""Reads and returns an signed, varint-encoded, 32-bit integer."""
83
return self._stream.ReadVarUInt32()
86
"""Reads and returns an signed, varint-encoded,64-bit integer."""
87
return self._stream.ReadVarUInt64()
90
"""Reads and returns a signed, zigzag-encoded, varint-encoded,
92
return wire_format.ZigZagDecode(self._stream.ReadVarUInt32())
95
"""Reads and returns a signed, zigzag-encoded, varint-encoded,
97
return wire_format.ZigZagDecode(self._stream.ReadVarUInt64())
99
def ReadFixed32(self):
100
"""Reads and returns an unsigned, fixed-width, 32-bit integer."""
101
return self._stream.ReadLittleEndian32()
103
def ReadFixed64(self):
104
"""Reads and returns an unsigned, fixed-width, 64-bit integer."""
105
return self._stream.ReadLittleEndian64()
107
def ReadSFixed32(self):
108
"""Reads and returns a signed, fixed-width, 32-bit integer."""
109
value = self._stream.ReadLittleEndian32()
110
if value >= (1 << 31):
114
def ReadSFixed64(self):
115
"""Reads and returns a signed, fixed-width, 64-bit integer."""
116
value = self._stream.ReadLittleEndian64()
117
if value >= (1 << 63):
122
"""Reads and returns a 4-byte floating-point number."""
123
serialized = self._stream.ReadString(4)
124
return struct.unpack('f', serialized)[0]
126
def ReadDouble(self):
127
"""Reads and returns an 8-byte floating-point number."""
128
serialized = self._stream.ReadString(8)
129
return struct.unpack('d', serialized)[0]
132
"""Reads and returns a bool."""
133
i = self._stream.ReadVarUInt32()
137
"""Reads and returns an enum value."""
138
return self._stream.ReadVarUInt32()
140
def ReadString(self):
141
"""Reads and returns a length-delimited string."""
142
length = self._stream.ReadVarUInt32()
143
return self._stream.ReadString(length)
146
"""Reads and returns a length-delimited byte sequence."""
147
return self.ReadString()
149
def ReadMessageInto(self, msg):
150
"""Calls msg.MergeFromString() to merge
151
length-delimited serialized message data into |msg|.
153
REQUIRES: The decoder must be positioned at the serialized "length"
154
prefix to a length-delmiited serialized message.
156
POSTCONDITION: The decoder is positioned just after the
157
serialized message, and we have merged those serialized
160
length = self._stream.ReadVarUInt32()
161
sub_buffer = self._stream.GetSubBuffer(length)
162
num_bytes_used = msg.MergeFromString(sub_buffer)
163
if num_bytes_used != length:
164
raise message.DecodeError(
165
'Submessage told to deserialize from %d-byte encoding, '
166
'but used only %d bytes' % (length, num_bytes_used))
167
self._stream.SkipBytes(num_bytes_used)
169
def ReadGroupInto(self, expected_field_number, group):
170
"""Calls group.MergeFromString() to merge
171
END_GROUP-delimited serialized message data into |group|.
172
We'll raise an exception if we don't find an END_GROUP
173
tag immediately after the serialized message contents.
175
REQUIRES: The decoder is positioned just after the START_GROUP
178
POSTCONDITION: The decoder is positioned just after the
179
END_GROUP tag for this group, and we have merged
180
the contents of the group into |group|.
182
sub_buffer = self._stream.GetSubBuffer() # No a priori length limit.
183
num_bytes_used = group.MergeFromString(sub_buffer)
184
if num_bytes_used < 0:
185
raise message.DecodeError('Group message reported negative bytes read.')
186
self._stream.SkipBytes(num_bytes_used)
187
field_number, field_type = self.ReadFieldNumberAndWireType()
188
if field_type != wire_format.WIRETYPE_END_GROUP:
189
raise message.DecodeError('Group message did not end with an END_GROUP.')
190
if field_number != expected_field_number:
191
raise message.DecodeError('END_GROUP tag had field '
192
'number %d, was expecting field number %d' % (
193
field_number, expected_field_number))
194
# We're now positioned just after the END_GROUP tag. Perfect.