diff options
Diffstat (limited to 'src/google/protobuf/io/coded_stream.cc')
-rw-r--r-- | src/google/protobuf/io/coded_stream.cc | 90 |
1 files changed, 49 insertions, 41 deletions
diff --git a/src/google/protobuf/io/coded_stream.cc b/src/google/protobuf/io/coded_stream.cc index 0851ff0c..547c5c64 100644 --- a/src/google/protobuf/io/coded_stream.cc +++ b/src/google/protobuf/io/coded_stream.cc @@ -49,6 +49,8 @@ #include <google/protobuf/stubs/stl_util.h> +#include <google/protobuf/port_def.inc> + namespace google { namespace protobuf { namespace io { @@ -121,7 +123,7 @@ CodedInputStream::Limit CodedInputStream::PushLimit(int byte_limit) { // security: byte_limit is possibly evil, so check for negative values // and overflow. Also check that the new requested limit is before the // previous limit; otherwise we continue to enforce the previous limit. - if (GOOGLE_PREDICT_TRUE(byte_limit >= 0 && + if (PROTOBUF_PREDICT_TRUE(byte_limit >= 0 && byte_limit <= INT_MAX - current_position && byte_limit < current_limit_ - current_position)) { current_limit_ = current_position + byte_limit; @@ -191,7 +193,7 @@ void CodedInputStream::PrintTotalBytesLimitError() { "big (more than " << total_bytes_limit_ << " bytes). To increase the limit (or to disable these " "warnings), see CodedInputStream::SetTotalBytesLimit() " - "in google/protobuf/io/coded_stream.h."; + "in net/proto2/io/public/coded_stream.h."; } bool CodedInputStream::SkipFallback(int count, int original_buffer_size) { @@ -312,11 +314,25 @@ bool CodedInputStream::ReadLittleEndian64Fallback(uint64* value) { namespace { +// Decodes varint64 with known size, N, and returns next pointer. Knowing N at +// compile time, compiler can generate optimal code. For example, instead of +// subtracting 0x80 at each iteration, it subtracts properly shifted mask once. +template <size_t N> +const uint8* DecodeVarint64KnownSize(const uint8* buffer, uint64* value) { + GOOGLE_DCHECK_GT(N, 0); + uint64 result = static_cast<uint64>(buffer[N - 1]) << (7 * (N - 1)); + for (int i = 0, offset = 0; i < N - 1; i++, offset += 7) { + result += static_cast<uint64>(buffer[i] - 0x80) << offset; + } + *value = result; + return buffer + N; +} + // Read a varint from the given buffer, write it to *value, and return a pair. // The first part of the pair is true iff the read was successful. The second // part is buffer + (number of bytes read). This function is always inlined, // so returning a pair is costless. -GOOGLE_PROTOBUF_ATTRIBUTE_ALWAYS_INLINE +PROTOBUF_ALWAYS_INLINE ::std::pair<bool, const uint8*> ReadVarint32FromArray( uint32 first_byte, const uint8* buffer, uint32* value); @@ -354,47 +370,39 @@ inline ::std::pair<bool, const uint8*> ReadVarint32FromArray( return std::make_pair(true, ptr); } -GOOGLE_PROTOBUF_ATTRIBUTE_ALWAYS_INLINE::std::pair<bool, const uint8*> -ReadVarint64FromArray(const uint8* buffer, uint64* value); +PROTOBUF_ALWAYS_INLINE::std::pair<bool, const uint8*> ReadVarint64FromArray( + const uint8* buffer, uint64* value); inline ::std::pair<bool, const uint8*> ReadVarint64FromArray( const uint8* buffer, uint64* value) { - const uint8* ptr = buffer; - uint32 b; - - // Splitting into 32-bit pieces gives better performance on 32-bit - // processors. - uint32 part0 = 0, part1 = 0, part2 = 0; - - b = *(ptr++); part0 = b ; if (!(b & 0x80)) goto done; - part0 -= 0x80; - b = *(ptr++); part0 += b << 7; if (!(b & 0x80)) goto done; - part0 -= 0x80 << 7; - b = *(ptr++); part0 += b << 14; if (!(b & 0x80)) goto done; - part0 -= 0x80 << 14; - b = *(ptr++); part0 += b << 21; if (!(b & 0x80)) goto done; - part0 -= 0x80 << 21; - b = *(ptr++); part1 = b ; if (!(b & 0x80)) goto done; - part1 -= 0x80; - b = *(ptr++); part1 += b << 7; if (!(b & 0x80)) goto done; - part1 -= 0x80 << 7; - b = *(ptr++); part1 += b << 14; if (!(b & 0x80)) goto done; - part1 -= 0x80 << 14; - b = *(ptr++); part1 += b << 21; if (!(b & 0x80)) goto done; - part1 -= 0x80 << 21; - b = *(ptr++); part2 = b ; if (!(b & 0x80)) goto done; - part2 -= 0x80; - b = *(ptr++); part2 += b << 7; if (!(b & 0x80)) goto done; - // "part2 -= 0x80 << 7" is irrelevant because (0x80 << 7) << 56 is 0. - - // We have overrun the maximum size of a varint (10 bytes). Assume - // the data is corrupt. - return std::make_pair(false, ptr); + // Assumes varint64 is at least 2 bytes. + GOOGLE_DCHECK_GE(buffer[0], 128); + + const uint8* next; + if (buffer[1] < 128) { + next = DecodeVarint64KnownSize<2>(buffer, value); + } else if (buffer[2] < 128) { + next = DecodeVarint64KnownSize<3>(buffer, value); + } else if (buffer[3] < 128) { + next = DecodeVarint64KnownSize<4>(buffer, value); + } else if (buffer[4] < 128) { + next = DecodeVarint64KnownSize<5>(buffer, value); + } else if (buffer[5] < 128) { + next = DecodeVarint64KnownSize<6>(buffer, value); + } else if (buffer[6] < 128) { + next = DecodeVarint64KnownSize<7>(buffer, value); + } else if (buffer[7] < 128) { + next = DecodeVarint64KnownSize<8>(buffer, value); + } else if (buffer[8] < 128) { + next = DecodeVarint64KnownSize<9>(buffer, value); + } else if (buffer[9] < 128) { + next = DecodeVarint64KnownSize<10>(buffer, value); + } else { + // We have overrun the maximum size of a varint (10 bytes). Assume + // the data is corrupt. + return std::make_pair(false, buffer + 11); + } - done: - *value = (static_cast<uint64>(part0)) | - (static_cast<uint64>(part1) << 28) | - (static_cast<uint64>(part2) << 56); - return std::make_pair(true, ptr); + return std::make_pair(true, next); } } // namespace |