Index: third_party/protobuf/python/google/protobuf/internal/encoder.py |
diff --git a/third_party/protobuf/python/google/protobuf/internal/encoder.py b/third_party/protobuf/python/google/protobuf/internal/encoder.py |
index aa05d5b3301308e419a9b48cc31442ac3f7f7540..777975e82e042b277be6ff6b1dae3b4f5cf6fa57 100755 |
--- a/third_party/protobuf/python/google/protobuf/internal/encoder.py |
+++ b/third_party/protobuf/python/google/protobuf/internal/encoder.py |
@@ -70,6 +70,12 @@ import struct |
from google.protobuf.internal import wire_format |
+# This will overflow and thus become IEEE-754 "infinity". We would use |
+# "float('inf')" but it doesn't work on Windows pre-Python-2.6. |
+_POS_INF = 1e10000 |
+_NEG_INF = -_POS_INF |
+ |
+ |
def _VarintSize(value): |
"""Compute the size of a varint value.""" |
if value <= 0x7f: return 1 |
@@ -502,6 +508,83 @@ def _StructPackEncoder(wire_type, format): |
return SpecificEncoder |
+def _FloatingPointEncoder(wire_type, format): |
+ """Return a constructor for an encoder for float fields. |
+ |
+ This is like StructPackEncoder, but catches errors that may be due to |
+ passing non-finite floating-point values to struct.pack, and makes a |
+ second attempt to encode those values. |
+ |
+ Args: |
+ wire_type: The field's wire type, for encoding tags. |
+ format: The format string to pass to struct.pack(). |
+ """ |
+ |
+ value_size = struct.calcsize(format) |
+ if value_size == 4: |
+ def EncodeNonFiniteOrRaise(write, value): |
+ # Remember that the serialized form uses little-endian byte order. |
+ if value == _POS_INF: |
+ write('\x00\x00\x80\x7F') |
+ elif value == _NEG_INF: |
+ write('\x00\x00\x80\xFF') |
+ elif value != value: # NaN |
+ write('\x00\x00\xC0\x7F') |
+ else: |
+ raise |
+ elif value_size == 8: |
+ def EncodeNonFiniteOrRaise(write, value): |
+ if value == _POS_INF: |
+ write('\x00\x00\x00\x00\x00\x00\xF0\x7F') |
+ elif value == _NEG_INF: |
+ write('\x00\x00\x00\x00\x00\x00\xF0\xFF') |
+ elif value != value: # NaN |
+ write('\x00\x00\x00\x00\x00\x00\xF8\x7F') |
+ else: |
+ raise |
+ else: |
+ raise ValueError('Can\'t encode floating-point values that are ' |
+ '%d bytes long (only 4 or 8)' % value_size) |
+ |
+ def SpecificEncoder(field_number, is_repeated, is_packed): |
+ local_struct_pack = struct.pack |
+ if is_packed: |
+ tag_bytes = TagBytes(field_number, wire_format.WIRETYPE_LENGTH_DELIMITED) |
+ local_EncodeVarint = _EncodeVarint |
+ def EncodePackedField(write, value): |
+ write(tag_bytes) |
+ local_EncodeVarint(write, len(value) * value_size) |
+ for element in value: |
+ # This try/except block is going to be faster than any code that |
+ # we could write to check whether element is finite. |
+ try: |
+ write(local_struct_pack(format, element)) |
+ except SystemError: |
+ EncodeNonFiniteOrRaise(write, element) |
+ return EncodePackedField |
+ elif is_repeated: |
+ tag_bytes = TagBytes(field_number, wire_type) |
+ def EncodeRepeatedField(write, value): |
+ for element in value: |
+ write(tag_bytes) |
+ try: |
+ write(local_struct_pack(format, element)) |
+ except SystemError: |
+ EncodeNonFiniteOrRaise(write, element) |
+ return EncodeRepeatedField |
+ else: |
+ tag_bytes = TagBytes(field_number, wire_type) |
+ def EncodeField(write, value): |
+ write(tag_bytes) |
+ try: |
+ write(local_struct_pack(format, value)) |
+ except SystemError: |
+ EncodeNonFiniteOrRaise(write, value) |
+ return EncodeField |
+ |
+ return SpecificEncoder |
+ |
+ |
# ==================================================================== |
# Here we declare an encoder constructor for each field type. These work |
# very similarly to sizer constructors, described earlier. |
@@ -525,8 +608,8 @@ Fixed32Encoder = _StructPackEncoder(wire_format.WIRETYPE_FIXED32, '<I') |
Fixed64Encoder = _StructPackEncoder(wire_format.WIRETYPE_FIXED64, '<Q') |
SFixed32Encoder = _StructPackEncoder(wire_format.WIRETYPE_FIXED32, '<i') |
SFixed64Encoder = _StructPackEncoder(wire_format.WIRETYPE_FIXED64, '<q') |
-FloatEncoder = _StructPackEncoder(wire_format.WIRETYPE_FIXED32, '<f') |
-DoubleEncoder = _StructPackEncoder(wire_format.WIRETYPE_FIXED64, '<d') |
+FloatEncoder = _FloatingPointEncoder(wire_format.WIRETYPE_FIXED32, '<f') |
+DoubleEncoder = _FloatingPointEncoder(wire_format.WIRETYPE_FIXED64, '<d') |
def BoolEncoder(field_number, is_repeated, is_packed): |