| # 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. |
| |
| """InputStream is the primitive interface for reading bits from the wire. |
| |
| All protocol buffer deserialization can be expressed in terms of |
| the InputStream primitives provided here. |
| """ |
| |
| __author__ = 'robinson@google.com (Will Robinson)' |
| |
| import struct |
| from array import array |
| from froofle.protobuf import message |
| from froofle.protobuf.internal import wire_format |
| |
| |
| # Note that much of this code is ported from //net/proto/ProtocolBuffer, and |
| # that the interface is strongly inspired by CodedInputStream from the C++ |
| # proto2 implementation. |
| |
| |
| class InputStreamBuffer(object): |
| |
| """Contains all logic for reading bits, and dealing with stream position. |
| |
| If an InputStream method ever raises an exception, the stream is left |
| in an indeterminate state and is not safe for further use. |
| """ |
| |
| def __init__(self, s): |
| # What we really want is something like array('B', s), where elements we |
| # read from the array are already given to us as one-byte integers. BUT |
| # using array() instead of buffer() would force full string copies to result |
| # from each GetSubBuffer() call. |
| # |
| # So, if the N serialized bytes of a single protocol buffer object are |
| # split evenly between 2 child messages, and so on recursively, using |
| # array('B', s) instead of buffer() would incur an additional N*logN bytes |
| # copied during deserialization. |
| # |
| # The higher constant overhead of having to ord() for every byte we read |
| # from the buffer in _ReadVarintHelper() could definitely lead to worse |
| # performance in many real-world scenarios, even if the asymptotic |
| # complexity is better. However, our real answer is that the mythical |
| # Python/C extension module output mode for the protocol compiler will |
| # be blazing-fast and will eliminate most use of this class anyway. |
| self._buffer = buffer(s) |
| self._pos = 0 |
| |
| def EndOfStream(self): |
| """Returns true iff we're at the end of the stream. |
| If this returns true, then a call to any other InputStream method |
| will raise an exception. |
| """ |
| return self._pos >= len(self._buffer) |
| |
| def Position(self): |
| """Returns the current position in the stream, or equivalently, the |
| number of bytes read so far. |
| """ |
| return self._pos |
| |
| def GetSubBuffer(self, size=None): |
| """Returns a sequence-like object that represents a portion of our |
| underlying sequence. |
| |
| Position 0 in the returned object corresponds to self.Position() |
| in this stream. |
| |
| If size is specified, then the returned object ends after the |
| next "size" bytes in this stream. If size is not specified, |
| then the returned object ends at the end of this stream. |
| |
| We guarantee that the returned object R supports the Python buffer |
| interface (and thus that the call buffer(R) will work). |
| |
| Note that the returned buffer is read-only. |
| |
| The intended use for this method is for nested-message and nested-group |
| deserialization, where we want to make a recursive MergeFromString() |
| call on the portion of the original sequence that contains the serialized |
| nested message. (And we'd like to do so without making unnecessary string |
| copies). |
| |
| REQUIRES: size is nonnegative. |
| """ |
| # Note that buffer() doesn't perform any actual string copy. |
| if size is None: |
| return buffer(self._buffer, self._pos) |
| else: |
| if size < 0: |
| raise message.DecodeError('Negative size %d' % size) |
| return buffer(self._buffer, self._pos, size) |
| |
| def SkipBytes(self, num_bytes): |
| """Skip num_bytes bytes ahead, or go to the end of the stream, whichever |
| comes first. |
| |
| REQUIRES: num_bytes is nonnegative. |
| """ |
| if num_bytes < 0: |
| raise message.DecodeError('Negative num_bytes %d' % num_bytes) |
| self._pos += num_bytes |
| self._pos = min(self._pos, len(self._buffer)) |
| |
| def ReadBytes(self, size): |
| """Reads up to 'size' bytes from the stream, stopping early |
| only if we reach the end of the stream. Returns the bytes read |
| as a string. |
| """ |
| if size < 0: |
| raise message.DecodeError('Negative size %d' % size) |
| s = (self._buffer[self._pos : self._pos + size]) |
| self._pos += len(s) # Only advance by the number of bytes actually read. |
| return s |
| |
| def ReadLittleEndian32(self): |
| """Interprets the next 4 bytes of the stream as a little-endian |
| encoded, unsiged 32-bit integer, and returns that integer. |
| """ |
| try: |
| i = struct.unpack(wire_format.FORMAT_UINT32_LITTLE_ENDIAN, |
| self._buffer[self._pos : self._pos + 4]) |
| self._pos += 4 |
| return i[0] # unpack() result is a 1-element tuple. |
| except struct.error, e: |
| raise message.DecodeError(e) |
| |
| def ReadLittleEndian64(self): |
| """Interprets the next 8 bytes of the stream as a little-endian |
| encoded, unsiged 64-bit integer, and returns that integer. |
| """ |
| try: |
| i = struct.unpack(wire_format.FORMAT_UINT64_LITTLE_ENDIAN, |
| self._buffer[self._pos : self._pos + 8]) |
| self._pos += 8 |
| return i[0] # unpack() result is a 1-element tuple. |
| except struct.error, e: |
| raise message.DecodeError(e) |
| |
| def ReadVarint32(self): |
| """Reads a varint from the stream, interprets this varint |
| as a signed, 32-bit integer, and returns the integer. |
| """ |
| i = self.ReadVarint64() |
| if not wire_format.INT32_MIN <= i <= wire_format.INT32_MAX: |
| raise message.DecodeError('Value out of range for int32: %d' % i) |
| return int(i) |
| |
| def ReadVarUInt32(self): |
| """Reads a varint from the stream, interprets this varint |
| as an unsigned, 32-bit integer, and returns the integer. |
| """ |
| i = self.ReadVarUInt64() |
| if i > wire_format.UINT32_MAX: |
| raise message.DecodeError('Value out of range for uint32: %d' % i) |
| return i |
| |
| def ReadVarint64(self): |
| """Reads a varint from the stream, interprets this varint |
| as a signed, 64-bit integer, and returns the integer. |
| """ |
| i = self.ReadVarUInt64() |
| if i > wire_format.INT64_MAX: |
| i -= (1 << 64) |
| return i |
| |
| def ReadVarUInt64(self): |
| """Reads a varint from the stream, interprets this varint |
| as an unsigned, 64-bit integer, and returns the integer. |
| """ |
| i = self._ReadVarintHelper() |
| if not 0 <= i <= wire_format.UINT64_MAX: |
| raise message.DecodeError('Value out of range for uint64: %d' % i) |
| return i |
| |
| def _ReadVarintHelper(self): |
| """Helper for the various varint-reading methods above. |
| Reads an unsigned, varint-encoded integer from the stream and |
| returns this integer. |
| |
| Does no bounds checking except to ensure that we read at most as many bytes |
| as could possibly be present in a varint-encoded 64-bit number. |
| """ |
| result = 0 |
| shift = 0 |
| while 1: |
| if shift >= 64: |
| raise message.DecodeError('Too many bytes when decoding varint.') |
| try: |
| b = ord(self._buffer[self._pos]) |
| except IndexError: |
| raise message.DecodeError('Truncated varint.') |
| self._pos += 1 |
| result |= ((b & 0x7f) << shift) |
| shift += 7 |
| if not (b & 0x80): |
| return result |
| |
| class InputStreamArray(object): |
| def __init__(self, s): |
| self._buffer = array('B', s) |
| self._pos = 0 |
| |
| def EndOfStream(self): |
| return self._pos >= len(self._buffer) |
| |
| def Position(self): |
| return self._pos |
| |
| def GetSubBuffer(self, size=None): |
| if size is None: |
| return self._buffer[self._pos : ].tostring() |
| else: |
| if size < 0: |
| raise message.DecodeError('Negative size %d' % size) |
| return self._buffer[self._pos : self._pos + size].tostring() |
| |
| def SkipBytes(self, num_bytes): |
| if num_bytes < 0: |
| raise message.DecodeError('Negative num_bytes %d' % num_bytes) |
| self._pos += num_bytes |
| self._pos = min(self._pos, len(self._buffer)) |
| |
| def ReadBytes(self, size): |
| if size < 0: |
| raise message.DecodeError('Negative size %d' % size) |
| s = self._buffer[self._pos : self._pos + size].tostring() |
| self._pos += len(s) # Only advance by the number of bytes actually read. |
| return s |
| |
| def ReadLittleEndian32(self): |
| try: |
| i = struct.unpack(wire_format.FORMAT_UINT32_LITTLE_ENDIAN, |
| self._buffer[self._pos : self._pos + 4]) |
| self._pos += 4 |
| return i[0] # unpack() result is a 1-element tuple. |
| except struct.error, e: |
| raise message.DecodeError(e) |
| |
| def ReadLittleEndian64(self): |
| try: |
| i = struct.unpack(wire_format.FORMAT_UINT64_LITTLE_ENDIAN, |
| self._buffer[self._pos : self._pos + 8]) |
| self._pos += 8 |
| return i[0] # unpack() result is a 1-element tuple. |
| except struct.error, e: |
| raise message.DecodeError(e) |
| |
| def ReadVarint32(self): |
| i = self.ReadVarint64() |
| if not wire_format.INT32_MIN <= i <= wire_format.INT32_MAX: |
| raise message.DecodeError('Value out of range for int32: %d' % i) |
| return int(i) |
| |
| def ReadVarUInt32(self): |
| i = self.ReadVarUInt64() |
| if i > wire_format.UINT32_MAX: |
| raise message.DecodeError('Value out of range for uint32: %d' % i) |
| return i |
| |
| def ReadVarint64(self): |
| i = self.ReadVarUInt64() |
| if i > wire_format.INT64_MAX: |
| i -= (1 << 64) |
| return i |
| |
| def ReadVarUInt64(self): |
| i = self._ReadVarintHelper() |
| if not 0 <= i <= wire_format.UINT64_MAX: |
| raise message.DecodeError('Value out of range for uint64: %d' % i) |
| return i |
| |
| def _ReadVarintHelper(self): |
| result = 0 |
| shift = 0 |
| while 1: |
| if shift >= 64: |
| raise message.DecodeError('Too many bytes when decoding varint.') |
| try: |
| b = self._buffer[self._pos] |
| except IndexError: |
| raise message.DecodeError('Truncated varint.') |
| self._pos += 1 |
| result |= ((b & 0x7f) << shift) |
| shift += 7 |
| if not (b & 0x80): |
| return result |
| |
| try: |
| buffer("") |
| InputStream = InputStreamBuffer |
| except NotImplementedError: |
| # Google App Engine: dev_appserver.py |
| InputStream = InputStreamArray |
| except RuntimeError: |
| # Google App Engine: production |
| InputStream = InputStreamArray |