blob: 26a26dcf525fba7f32cfaada6fe223ec66b9d29c [file] [log] [blame]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001# Protocol Buffers - Google's data interchange format
2# Copyright 2008 Google Inc. All rights reserved.
3# http://code.google.com/p/protobuf/
4#
5# Redistribution and use in source and binary forms, with or without
6# modification, are permitted provided that the following conditions are
7# met:
8#
9# * Redistributions of source code must retain the above copyright
10# notice, this list of conditions and the following disclaimer.
11# * Redistributions in binary form must reproduce the above
12# copyright notice, this list of conditions and the following disclaimer
13# in the documentation and/or other materials provided with the
14# distribution.
15# * Neither the name of Google Inc. nor the names of its
16# contributors may be used to endorse or promote products derived from
17# this software without specific prior written permission.
18#
19# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31"""InputStream is the primitive interface for reading bits from the wire.
32
33All protocol buffer deserialization can be expressed in terms of
34the InputStream primitives provided here.
35"""
36
37__author__ = 'robinson@google.com (Will Robinson)'
38
39import struct
40from array import array
41from froofle.protobuf import message
42from froofle.protobuf.internal import wire_format
43
44
45# Note that much of this code is ported from //net/proto/ProtocolBuffer, and
46# that the interface is strongly inspired by CodedInputStream from the C++
47# proto2 implementation.
48
49
50class InputStreamBuffer(object):
51
52 """Contains all logic for reading bits, and dealing with stream position.
53
54 If an InputStream method ever raises an exception, the stream is left
55 in an indeterminate state and is not safe for further use.
56 """
57
58 def __init__(self, s):
59 # What we really want is something like array('B', s), where elements we
60 # read from the array are already given to us as one-byte integers. BUT
61 # using array() instead of buffer() would force full string copies to result
62 # from each GetSubBuffer() call.
63 #
64 # So, if the N serialized bytes of a single protocol buffer object are
65 # split evenly between 2 child messages, and so on recursively, using
66 # array('B', s) instead of buffer() would incur an additional N*logN bytes
67 # copied during deserialization.
68 #
69 # The higher constant overhead of having to ord() for every byte we read
70 # from the buffer in _ReadVarintHelper() could definitely lead to worse
71 # performance in many real-world scenarios, even if the asymptotic
72 # complexity is better. However, our real answer is that the mythical
73 # Python/C extension module output mode for the protocol compiler will
74 # be blazing-fast and will eliminate most use of this class anyway.
75 self._buffer = buffer(s)
76 self._pos = 0
77
78 def EndOfStream(self):
79 """Returns true iff we're at the end of the stream.
80 If this returns true, then a call to any other InputStream method
81 will raise an exception.
82 """
83 return self._pos >= len(self._buffer)
84
85 def Position(self):
86 """Returns the current position in the stream, or equivalently, the
87 number of bytes read so far.
88 """
89 return self._pos
90
91 def GetSubBuffer(self, size=None):
92 """Returns a sequence-like object that represents a portion of our
93 underlying sequence.
94
95 Position 0 in the returned object corresponds to self.Position()
96 in this stream.
97
98 If size is specified, then the returned object ends after the
99 next "size" bytes in this stream. If size is not specified,
100 then the returned object ends at the end of this stream.
101
102 We guarantee that the returned object R supports the Python buffer
103 interface (and thus that the call buffer(R) will work).
104
105 Note that the returned buffer is read-only.
106
107 The intended use for this method is for nested-message and nested-group
108 deserialization, where we want to make a recursive MergeFromString()
109 call on the portion of the original sequence that contains the serialized
110 nested message. (And we'd like to do so without making unnecessary string
111 copies).
112
113 REQUIRES: size is nonnegative.
114 """
115 # Note that buffer() doesn't perform any actual string copy.
116 if size is None:
117 return buffer(self._buffer, self._pos)
118 else:
119 if size < 0:
120 raise message.DecodeError('Negative size %d' % size)
121 return buffer(self._buffer, self._pos, size)
122
123 def SkipBytes(self, num_bytes):
124 """Skip num_bytes bytes ahead, or go to the end of the stream, whichever
125 comes first.
126
127 REQUIRES: num_bytes is nonnegative.
128 """
129 if num_bytes < 0:
130 raise message.DecodeError('Negative num_bytes %d' % num_bytes)
131 self._pos += num_bytes
132 self._pos = min(self._pos, len(self._buffer))
133
134 def ReadBytes(self, size):
135 """Reads up to 'size' bytes from the stream, stopping early
136 only if we reach the end of the stream. Returns the bytes read
137 as a string.
138 """
139 if size < 0:
140 raise message.DecodeError('Negative size %d' % size)
141 s = (self._buffer[self._pos : self._pos + size])
142 self._pos += len(s) # Only advance by the number of bytes actually read.
143 return s
144
145 def ReadLittleEndian32(self):
146 """Interprets the next 4 bytes of the stream as a little-endian
147 encoded, unsiged 32-bit integer, and returns that integer.
148 """
149 try:
150 i = struct.unpack(wire_format.FORMAT_UINT32_LITTLE_ENDIAN,
151 self._buffer[self._pos : self._pos + 4])
152 self._pos += 4
153 return i[0] # unpack() result is a 1-element tuple.
154 except struct.error, e:
155 raise message.DecodeError(e)
156
157 def ReadLittleEndian64(self):
158 """Interprets the next 8 bytes of the stream as a little-endian
159 encoded, unsiged 64-bit integer, and returns that integer.
160 """
161 try:
162 i = struct.unpack(wire_format.FORMAT_UINT64_LITTLE_ENDIAN,
163 self._buffer[self._pos : self._pos + 8])
164 self._pos += 8
165 return i[0] # unpack() result is a 1-element tuple.
166 except struct.error, e:
167 raise message.DecodeError(e)
168
169 def ReadVarint32(self):
170 """Reads a varint from the stream, interprets this varint
171 as a signed, 32-bit integer, and returns the integer.
172 """
173 i = self.ReadVarint64()
174 if not wire_format.INT32_MIN <= i <= wire_format.INT32_MAX:
175 raise message.DecodeError('Value out of range for int32: %d' % i)
176 return int(i)
177
178 def ReadVarUInt32(self):
179 """Reads a varint from the stream, interprets this varint
180 as an unsigned, 32-bit integer, and returns the integer.
181 """
182 i = self.ReadVarUInt64()
183 if i > wire_format.UINT32_MAX:
184 raise message.DecodeError('Value out of range for uint32: %d' % i)
185 return i
186
187 def ReadVarint64(self):
188 """Reads a varint from the stream, interprets this varint
189 as a signed, 64-bit integer, and returns the integer.
190 """
191 i = self.ReadVarUInt64()
192 if i > wire_format.INT64_MAX:
193 i -= (1 << 64)
194 return i
195
196 def ReadVarUInt64(self):
197 """Reads a varint from the stream, interprets this varint
198 as an unsigned, 64-bit integer, and returns the integer.
199 """
200 i = self._ReadVarintHelper()
201 if not 0 <= i <= wire_format.UINT64_MAX:
202 raise message.DecodeError('Value out of range for uint64: %d' % i)
203 return i
204
205 def _ReadVarintHelper(self):
206 """Helper for the various varint-reading methods above.
207 Reads an unsigned, varint-encoded integer from the stream and
208 returns this integer.
209
210 Does no bounds checking except to ensure that we read at most as many bytes
211 as could possibly be present in a varint-encoded 64-bit number.
212 """
213 result = 0
214 shift = 0
215 while 1:
216 if shift >= 64:
217 raise message.DecodeError('Too many bytes when decoding varint.')
218 try:
219 b = ord(self._buffer[self._pos])
220 except IndexError:
221 raise message.DecodeError('Truncated varint.')
222 self._pos += 1
223 result |= ((b & 0x7f) << shift)
224 shift += 7
225 if not (b & 0x80):
226 return result
227
228class InputStreamArray(object):
229 def __init__(self, s):
230 self._buffer = array('B', s)
231 self._pos = 0
232
233 def EndOfStream(self):
234 return self._pos >= len(self._buffer)
235
236 def Position(self):
237 return self._pos
238
239 def GetSubBuffer(self, size=None):
240 if size is None:
241 return self._buffer[self._pos : ].tostring()
242 else:
243 if size < 0:
244 raise message.DecodeError('Negative size %d' % size)
245 return self._buffer[self._pos : self._pos + size].tostring()
246
247 def SkipBytes(self, num_bytes):
248 if num_bytes < 0:
249 raise message.DecodeError('Negative num_bytes %d' % num_bytes)
250 self._pos += num_bytes
251 self._pos = min(self._pos, len(self._buffer))
252
253 def ReadBytes(self, size):
254 if size < 0:
255 raise message.DecodeError('Negative size %d' % size)
256 s = self._buffer[self._pos : self._pos + size].tostring()
257 self._pos += len(s) # Only advance by the number of bytes actually read.
258 return s
259
260 def ReadLittleEndian32(self):
261 try:
262 i = struct.unpack(wire_format.FORMAT_UINT32_LITTLE_ENDIAN,
263 self._buffer[self._pos : self._pos + 4])
264 self._pos += 4
265 return i[0] # unpack() result is a 1-element tuple.
266 except struct.error, e:
267 raise message.DecodeError(e)
268
269 def ReadLittleEndian64(self):
270 try:
271 i = struct.unpack(wire_format.FORMAT_UINT64_LITTLE_ENDIAN,
272 self._buffer[self._pos : self._pos + 8])
273 self._pos += 8
274 return i[0] # unpack() result is a 1-element tuple.
275 except struct.error, e:
276 raise message.DecodeError(e)
277
278 def ReadVarint32(self):
279 i = self.ReadVarint64()
280 if not wire_format.INT32_MIN <= i <= wire_format.INT32_MAX:
281 raise message.DecodeError('Value out of range for int32: %d' % i)
282 return int(i)
283
284 def ReadVarUInt32(self):
285 i = self.ReadVarUInt64()
286 if i > wire_format.UINT32_MAX:
287 raise message.DecodeError('Value out of range for uint32: %d' % i)
288 return i
289
290 def ReadVarint64(self):
291 i = self.ReadVarUInt64()
292 if i > wire_format.INT64_MAX:
293 i -= (1 << 64)
294 return i
295
296 def ReadVarUInt64(self):
297 i = self._ReadVarintHelper()
298 if not 0 <= i <= wire_format.UINT64_MAX:
299 raise message.DecodeError('Value out of range for uint64: %d' % i)
300 return i
301
302 def _ReadVarintHelper(self):
303 result = 0
304 shift = 0
305 while 1:
306 if shift >= 64:
307 raise message.DecodeError('Too many bytes when decoding varint.')
308 try:
309 b = self._buffer[self._pos]
310 except IndexError:
311 raise message.DecodeError('Truncated varint.')
312 self._pos += 1
313 result |= ((b & 0x7f) << shift)
314 shift += 7
315 if not (b & 0x80):
316 return result
317
318try:
319 buffer("")
320 InputStream = InputStreamBuffer
321except NotImplementedError:
322 # Google App Engine: dev_appserver.py
323 InputStream = InputStreamArray
324except RuntimeError:
325 # Google App Engine: production
326 InputStream = InputStreamArray