aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/google/protobuf/io
diff options
context:
space:
mode:
authorGravatar Bo Yang <teboring@google.com>2015-05-21 14:28:59 -0700
committerGravatar Bo Yang <teboring@google.com>2015-05-21 19:32:02 -0700
commit5db217305f37a79eeccd70f000088a06ec82fcec (patch)
treebe53dcf0c0b47ef9178ab8a6fa5c1946ee84a28f /src/google/protobuf/io
parent56095026ccc2f755a6fdb296e30c3ddec8f556a2 (diff)
down-integrate internal changes
Diffstat (limited to 'src/google/protobuf/io')
-rw-r--r--src/google/protobuf/io/coded_stream.cc104
-rw-r--r--src/google/protobuf/io/coded_stream.h84
-rw-r--r--src/google/protobuf/io/coded_stream_inl.h17
-rw-r--r--src/google/protobuf/io/printer.cc72
-rw-r--r--src/google/protobuf/io/printer.h32
5 files changed, 241 insertions, 68 deletions
diff --git a/src/google/protobuf/io/coded_stream.cc b/src/google/protobuf/io/coded_stream.cc
index 93e1a22e..3b8650d6 100644
--- a/src/google/protobuf/io/coded_stream.cc
+++ b/src/google/protobuf/io/coded_stream.cc
@@ -156,6 +156,11 @@ CodedInputStream::IncrementRecursionDepthAndPushLimit(int byte_limit) {
return std::make_pair(PushLimit(byte_limit), --recursion_budget_);
}
+CodedInputStream::Limit CodedInputStream::ReadLengthAndPushLimit() {
+ uint32 length;
+ return PushLimit(ReadVarint32(&length) ? length : 0);
+}
+
bool CodedInputStream::DecrementRecursionDepthAndPopLimit(Limit limit) {
bool result = ConsumedEntireMessage();
PopLimit(limit);
@@ -164,6 +169,12 @@ bool CodedInputStream::DecrementRecursionDepthAndPopLimit(Limit limit) {
return result;
}
+bool CodedInputStream::CheckEntireMessageConsumedAndPopLimit(Limit limit) {
+ bool result = ConsumedEntireMessage();
+ PopLimit(limit);
+ return result;
+}
+
int CodedInputStream::BytesUntilLimit() const {
if (current_limit_ == INT_MAX) return -1;
int current_position = CurrentPosition();
@@ -245,20 +256,7 @@ bool CodedInputStream::GetDirectBufferPointer(const void** data, int* size) {
}
bool CodedInputStream::ReadRaw(void* buffer, int size) {
- int current_buffer_size;
- while ((current_buffer_size = BufferSize()) < size) {
- // Reading past end of buffer. Copy what we have, then refresh.
- memcpy(buffer, buffer_, current_buffer_size);
- buffer = reinterpret_cast<uint8*>(buffer) + current_buffer_size;
- size -= current_buffer_size;
- Advance(current_buffer_size);
- if (!Refresh()) return false;
- }
-
- memcpy(buffer, buffer_, size);
- Advance(size);
-
- return true;
+ return InternalReadRawInline(buffer, size);
}
bool CodedInputStream::ReadString(string* buffer, int size) {
@@ -336,17 +334,23 @@ bool CodedInputStream::ReadLittleEndian64Fallback(uint64* value) {
namespace {
-inline const uint8* ReadVarint32FromArray(
- const uint8* buffer, uint32* value) GOOGLE_ATTRIBUTE_ALWAYS_INLINE;
-inline const uint8* ReadVarint32FromArray(const uint8* buffer, uint32* value) {
+// 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.
+inline ::std::pair<bool, const uint8*> ReadVarint32FromArray(
+ uint32 first_byte, const uint8* buffer,
+ uint32* value) GOOGLE_ATTRIBUTE_ALWAYS_INLINE;
+inline ::std::pair<bool, const uint8*> ReadVarint32FromArray(
+ uint32 first_byte, const uint8* buffer, uint32* value) {
// Fast path: We have enough bytes left in the buffer to guarantee that
// this read won't cross the end, so we can skip the checks.
+ GOOGLE_DCHECK_EQ(*buffer, first_byte);
+ GOOGLE_DCHECK_EQ(first_byte & 0x80, 0x80) << first_byte;
const uint8* ptr = buffer;
uint32 b;
- uint32 result;
-
- b = *(ptr++); result = b ; if (!(b & 0x80)) goto done;
- result -= 0x80;
+ uint32 result = first_byte - 0x80;
+ ++ptr; // We just processed the first byte. Move on to the second.
b = *(ptr++); result += b << 7; if (!(b & 0x80)) goto done;
result -= 0x80 << 7;
b = *(ptr++); result += b << 14; if (!(b & 0x80)) goto done;
@@ -364,38 +368,42 @@ inline const uint8* ReadVarint32FromArray(const uint8* buffer, uint32* value) {
// We have overrun the maximum size of a varint (10 bytes). Assume
// the data is corrupt.
- return NULL;
+ return std::make_pair(false, ptr);
done:
*value = result;
- return ptr;
+ return std::make_pair(true, ptr);
}
} // namespace
bool CodedInputStream::ReadVarint32Slow(uint32* value) {
- uint64 result;
// Directly invoke ReadVarint64Fallback, since we already tried to optimize
// for one-byte varints.
- if (!ReadVarint64Fallback(&result)) return false;
- *value = (uint32)result;
- return true;
+ std::pair<uint64, bool> p = ReadVarint64Fallback();
+ *value = static_cast<uint32>(p.first);
+ return p.second;
}
-bool CodedInputStream::ReadVarint32Fallback(uint32* value) {
+int64 CodedInputStream::ReadVarint32Fallback(uint32 first_byte_or_zero) {
if (BufferSize() >= kMaxVarintBytes ||
// Optimization: We're also safe if the buffer is non-empty and it ends
// with a byte that would terminate a varint.
(buffer_end_ > buffer_ && !(buffer_end_[-1] & 0x80))) {
- const uint8* end = ReadVarint32FromArray(buffer_, value);
- if (end == NULL) return false;
- buffer_ = end;
- return true;
+ GOOGLE_DCHECK_NE(first_byte_or_zero, 0)
+ << "Caller should provide us with *buffer_ when buffer is non-empty";
+ uint32 temp;
+ ::std::pair<bool, const uint8*> p =
+ ReadVarint32FromArray(first_byte_or_zero, buffer_, &temp);
+ if (!p.first) return -1;
+ buffer_ = p.second;
+ return temp;
} else {
// Really slow case: we will incur the cost of an extra function call here,
// but moving this out of line reduces the size of this function, which
// improves the common case. In micro benchmarks, this is worth about 10-15%
- return ReadVarint32Slow(value);
+ uint32 temp;
+ return ReadVarint32Slow(&temp) ? static_cast<int64>(temp) : -1;
}
}
@@ -425,18 +433,24 @@ uint32 CodedInputStream::ReadTagSlow() {
return static_cast<uint32>(result);
}
-uint32 CodedInputStream::ReadTagFallback() {
+uint32 CodedInputStream::ReadTagFallback(uint32 first_byte_or_zero) {
const int buf_size = BufferSize();
if (buf_size >= kMaxVarintBytes ||
// Optimization: We're also safe if the buffer is non-empty and it ends
// with a byte that would terminate a varint.
(buf_size > 0 && !(buffer_end_[-1] & 0x80))) {
+ GOOGLE_DCHECK_EQ(first_byte_or_zero, buffer_[0]);
+ if (first_byte_or_zero == 0) {
+ ++buffer_;
+ return 0;
+ }
uint32 tag;
- const uint8* end = ReadVarint32FromArray(buffer_, &tag);
- if (end == NULL) {
+ ::std::pair<bool, const uint8*> p =
+ ReadVarint32FromArray(first_byte_or_zero, buffer_, &tag);
+ if (!p.first) {
return 0;
}
- buffer_ = end;
+ buffer_ = p.second;
return tag;
} else {
// We are commonly at a limit when attempting to read tags. Try to quickly
@@ -479,7 +493,7 @@ bool CodedInputStream::ReadVarint64Slow(uint64* value) {
return true;
}
-bool CodedInputStream::ReadVarint64Fallback(uint64* value) {
+std::pair<uint64, bool> CodedInputStream::ReadVarint64Fallback() {
if (BufferSize() >= kMaxVarintBytes ||
// Optimization: We're also safe if the buffer is non-empty and it ends
// with a byte that would terminate a varint.
@@ -517,16 +531,18 @@ bool CodedInputStream::ReadVarint64Fallback(uint64* value) {
// We have overrun the maximum size of a varint (10 bytes). The data
// must be corrupt.
- return false;
+ return std::make_pair(0, false);
done:
Advance(ptr - buffer_);
- *value = (static_cast<uint64>(part0) ) |
- (static_cast<uint64>(part1) << 28) |
- (static_cast<uint64>(part2) << 56);
- return true;
+ return std::make_pair((static_cast<uint64>(part0)) |
+ (static_cast<uint64>(part1) << 28) |
+ (static_cast<uint64>(part2) << 56),
+ true);
} else {
- return ReadVarint64Slow(value);
+ uint64 temp;
+ bool success = ReadVarint64Slow(&temp);
+ return std::make_pair(temp, success);
}
}
diff --git a/src/google/protobuf/io/coded_stream.h b/src/google/protobuf/io/coded_stream.h
index dea4b650..961c1a3f 100644
--- a/src/google/protobuf/io/coded_stream.h
+++ b/src/google/protobuf/io/coded_stream.h
@@ -197,6 +197,11 @@ class LIBPROTOBUF_EXPORT CodedInputStream {
// Read raw bytes, copying them into the given buffer.
bool ReadRaw(void* buffer, int size);
+ // Like the above, with inlined optimizations. This should only be used
+ // by the protobuf implementation.
+ inline bool InternalReadRawInline(void* buffer,
+ int size) GOOGLE_ATTRIBUTE_ALWAYS_INLINE;
+
// Like ReadRaw, but reads into a string.
//
// Implementation Note: ReadString() grows the string gradually as it
@@ -387,9 +392,14 @@ class LIBPROTOBUF_EXPORT CodedInputStream {
// under the limit, false if it has gone over.
bool IncrementRecursionDepth();
- // Decrements the recursion depth.
+ // Decrements the recursion depth if possible.
void DecrementRecursionDepth();
+ // Decrements the recursion depth blindly. This is faster than
+ // DecrementRecursionDepth(). It should be used only if all previous
+ // increments to recursion depth were successful.
+ void UnsafeDecrementRecursionDepth();
+
// Shorthand for make_pair(PushLimit(byte_limit), --recursion_budget_).
// Using this can reduce code size and complexity in some cases. The caller
// is expected to check that the second part of the result is non-negative (to
@@ -398,15 +408,25 @@ class LIBPROTOBUF_EXPORT CodedInputStream {
std::pair<CodedInputStream::Limit, int> IncrementRecursionDepthAndPushLimit(
int byte_limit);
+ // Shorthand for PushLimit(ReadVarint32(&length) ? length : 0).
+ Limit ReadLengthAndPushLimit();
+
// Helper that is equivalent to: {
// bool result = ConsumedEntireMessage();
// PopLimit(limit);
- // DecrementRecursionDepth();
+ // UnsafeDecrementRecursionDepth();
// return result; }
// Using this can reduce code size and complexity in some cases.
// Do not use unless the current recursion depth is greater than zero.
bool DecrementRecursionDepthAndPopLimit(Limit limit);
+ // Helper that is equivalent to: {
+ // bool result = ConsumedEntireMessage();
+ // PopLimit(limit);
+ // return result; }
+ // Using this can reduce code size and complexity in some cases.
+ bool CheckEntireMessageConsumedAndPopLimit(Limit limit);
+
// Extension Registry ----------------------------------------------
// ADVANCED USAGE: 99.9% of people can ignore this section.
//
@@ -568,9 +588,13 @@ class LIBPROTOBUF_EXPORT CodedInputStream {
// optimization. The Slow method is yet another fallback when the buffer is
// not large enough. Making the slow path out-of-line speeds up the common
// case by 10-15%. The slow path is fairly uncommon: it only triggers when a
- // message crosses multiple buffers.
- bool ReadVarint32Fallback(uint32* value);
- bool ReadVarint64Fallback(uint64* value);
+ // message crosses multiple buffers. Note: ReadVarint32Fallback() and
+ // ReadVarint64Fallback() are called frequently and generally not inlined, so
+ // they have been optimized to avoid "out" parameters. The former returns -1
+ // if it fails and the uint32 it read otherwise. The latter has a bool
+ // indicating success or failure as part of its return type.
+ int64 ReadVarint32Fallback(uint32 first_byte_or_zero);
+ std::pair<uint64, bool> ReadVarint64Fallback();
bool ReadVarint32Slow(uint32* value);
bool ReadVarint64Slow(uint64* value);
bool ReadLittleEndian32Fallback(uint32* value);
@@ -578,7 +602,7 @@ class LIBPROTOBUF_EXPORT CodedInputStream {
// Fallback/slow methods for reading tags. These do not update last_tag_,
// but will set legitimate_message_end_ if we are at the end of the input
// stream.
- uint32 ReadTagFallback();
+ uint32 ReadTagFallback(uint32 first_byte_or_zero);
uint32 ReadTagSlow();
bool ReadStringFallback(string* buffer, int size);
@@ -818,13 +842,18 @@ class LIBPROTOBUF_EXPORT CodedOutputStream {
// methods optimize for that case.
inline bool CodedInputStream::ReadVarint32(uint32* value) {
- if (GOOGLE_PREDICT_TRUE(buffer_ < buffer_end_) && *buffer_ < 0x80) {
- *value = *buffer_;
- Advance(1);
- return true;
- } else {
- return ReadVarint32Fallback(value);
+ uint32 v = 0;
+ if (GOOGLE_PREDICT_TRUE(buffer_ < buffer_end_)) {
+ v = *buffer_;
+ if (v < 0x80) {
+ *value = v;
+ Advance(1);
+ return true;
+ }
}
+ int64 result = ReadVarint32Fallback(v);
+ *value = static_cast<uint32>(result);
+ return result >= 0;
}
inline bool CodedInputStream::ReadVarint64(uint64* value) {
@@ -832,9 +861,10 @@ inline bool CodedInputStream::ReadVarint64(uint64* value) {
*value = *buffer_;
Advance(1);
return true;
- } else {
- return ReadVarint64Fallback(value);
}
+ std::pair<uint64, bool> p = ReadVarint64Fallback();
+ *value = p.first;
+ return p.second;
}
// static
@@ -903,14 +933,17 @@ inline bool CodedInputStream::ReadLittleEndian64(uint64* value) {
}
inline uint32 CodedInputStream::ReadTag() {
- if (GOOGLE_PREDICT_TRUE(buffer_ < buffer_end_) && buffer_[0] < 0x80) {
- last_tag_ = buffer_[0];
- Advance(1);
- return last_tag_;
- } else {
- last_tag_ = ReadTagFallback();
- return last_tag_;
+ uint32 v = 0;
+ if (GOOGLE_PREDICT_TRUE(buffer_ < buffer_end_)) {
+ v = *buffer_;
+ if (v < 0x80) {
+ last_tag_ = v;
+ Advance(1);
+ return v;
+ }
}
+ last_tag_ = ReadTagFallback(v);
+ return last_tag_;
}
inline std::pair<uint32, bool> CodedInputStream::ReadTagWithCutoff(
@@ -918,10 +951,12 @@ inline std::pair<uint32, bool> CodedInputStream::ReadTagWithCutoff(
// In performance-sensitive code we can expect cutoff to be a compile-time
// constant, and things like "cutoff >= kMax1ByteVarint" to be evaluated at
// compile time.
+ uint32 first_byte_or_zero = 0;
if (GOOGLE_PREDICT_TRUE(buffer_ < buffer_end_)) {
// Hot case: buffer_ non_empty, buffer_[0] in [1, 128).
// TODO(gpike): Is it worth rearranging this? E.g., if the number of fields
// is large enough then is it better to check for the two-byte case first?
+ first_byte_or_zero = buffer_[0];
if (static_cast<int8>(buffer_[0]) > 0) {
const uint32 kMax1ByteVarint = 0x7f;
uint32 tag = last_tag_ = buffer_[0];
@@ -948,7 +983,7 @@ inline std::pair<uint32, bool> CodedInputStream::ReadTagWithCutoff(
}
}
// Slow path
- last_tag_ = ReadTagFallback();
+ last_tag_ = ReadTagFallback(first_byte_or_zero);
return std::make_pair(last_tag_, static_cast<uint32>(last_tag_ - 1) < cutoff);
}
@@ -1177,6 +1212,11 @@ inline void CodedInputStream::DecrementRecursionDepth() {
if (recursion_budget_ < recursion_limit_) ++recursion_budget_;
}
+inline void CodedInputStream::UnsafeDecrementRecursionDepth() {
+ assert(recursion_budget_ < recursion_limit_);
+ ++recursion_budget_;
+}
+
inline void CodedInputStream::SetExtensionRegistry(const DescriptorPool* pool,
MessageFactory* factory) {
extension_pool_ = pool;
diff --git a/src/google/protobuf/io/coded_stream_inl.h b/src/google/protobuf/io/coded_stream_inl.h
index cd8d1746..fa20f208 100644
--- a/src/google/protobuf/io/coded_stream_inl.h
+++ b/src/google/protobuf/io/coded_stream_inl.h
@@ -66,6 +66,23 @@ inline bool CodedInputStream::InternalReadStringInline(string* buffer,
return ReadStringFallback(buffer, size);
}
+inline bool CodedInputStream::InternalReadRawInline(void* buffer, int size) {
+ int current_buffer_size;
+ while ((current_buffer_size = BufferSize()) < size) {
+ // Reading past end of buffer. Copy what we have, then refresh.
+ memcpy(buffer, buffer_, current_buffer_size);
+ buffer = reinterpret_cast<uint8*>(buffer) + current_buffer_size;
+ size -= current_buffer_size;
+ Advance(current_buffer_size);
+ if (!Refresh()) return false;
+ }
+
+ memcpy(buffer, buffer_, size);
+ Advance(size);
+
+ return true;
+}
+
} // namespace io
} // namespace protobuf
} // namespace google
diff --git a/src/google/protobuf/io/printer.cc b/src/google/protobuf/io/printer.cc
index e621ba1d..3ae8c268 100644
--- a/src/google/protobuf/io/printer.cc
+++ b/src/google/protobuf/io/printer.cc
@@ -155,6 +155,78 @@ void Printer::Print(const char* text,
Print(vars, text);
}
+void Printer::Print(const char* text,
+ const char* variable1, const string& value1,
+ const char* variable2, const string& value2,
+ const char* variable3, const string& value3,
+ const char* variable4, const string& value4,
+ const char* variable5, const string& value5) {
+ map<string, string> vars;
+ vars[variable1] = value1;
+ vars[variable2] = value2;
+ vars[variable3] = value3;
+ vars[variable4] = value4;
+ vars[variable5] = value5;
+ Print(vars, text);
+}
+
+void Printer::Print(const char* text,
+ const char* variable1, const string& value1,
+ const char* variable2, const string& value2,
+ const char* variable3, const string& value3,
+ const char* variable4, const string& value4,
+ const char* variable5, const string& value5,
+ const char* variable6, const string& value6) {
+ map<string, string> vars;
+ vars[variable1] = value1;
+ vars[variable2] = value2;
+ vars[variable3] = value3;
+ vars[variable4] = value4;
+ vars[variable5] = value5;
+ vars[variable6] = value6;
+ Print(vars, text);
+}
+
+void Printer::Print(const char* text,
+ const char* variable1, const string& value1,
+ const char* variable2, const string& value2,
+ const char* variable3, const string& value3,
+ const char* variable4, const string& value4,
+ const char* variable5, const string& value5,
+ const char* variable6, const string& value6,
+ const char* variable7, const string& value7) {
+ map<string, string> vars;
+ vars[variable1] = value1;
+ vars[variable2] = value2;
+ vars[variable3] = value3;
+ vars[variable4] = value4;
+ vars[variable5] = value5;
+ vars[variable6] = value6;
+ vars[variable7] = value7;
+ Print(vars, text);
+}
+
+void Printer::Print(const char* text,
+ const char* variable1, const string& value1,
+ const char* variable2, const string& value2,
+ const char* variable3, const string& value3,
+ const char* variable4, const string& value4,
+ const char* variable5, const string& value5,
+ const char* variable6, const string& value6,
+ const char* variable7, const string& value7,
+ const char* variable8, const string& value8) {
+ map<string, string> vars;
+ vars[variable1] = value1;
+ vars[variable2] = value2;
+ vars[variable3] = value3;
+ vars[variable4] = value4;
+ vars[variable5] = value5;
+ vars[variable6] = value6;
+ vars[variable7] = value7;
+ vars[variable8] = value8;
+ Print(vars, text);
+}
+
void Printer::Indent() {
indent_ += " ";
}
diff --git a/src/google/protobuf/io/printer.h b/src/google/protobuf/io/printer.h
index 92ce3409..f1490bbe 100644
--- a/src/google/protobuf/io/printer.h
+++ b/src/google/protobuf/io/printer.h
@@ -91,8 +91,36 @@ class LIBPROTOBUF_EXPORT Printer {
const char* variable2, const string& value2,
const char* variable3, const string& value3,
const char* variable4, const string& value4);
- // TODO(kenton): Overloaded versions with more variables? Three seems
- // to be enough.
+ // Like the first Print(), except the substitutions are given as parameters.
+ void Print(const char* text, const char* variable1, const string& value1,
+ const char* variable2, const string& value2,
+ const char* variable3, const string& value3,
+ const char* variable4, const string& value4,
+ const char* variable5, const string& value5);
+ // Like the first Print(), except the substitutions are given as parameters.
+ void Print(const char* text, const char* variable1, const string& value1,
+ const char* variable2, const string& value2,
+ const char* variable3, const string& value3,
+ const char* variable4, const string& value4,
+ const char* variable5, const string& value5,
+ const char* variable6, const string& value6);
+ // Like the first Print(), except the substitutions are given as parameters.
+ void Print(const char* text, const char* variable1, const string& value1,
+ const char* variable2, const string& value2,
+ const char* variable3, const string& value3,
+ const char* variable4, const string& value4,
+ const char* variable5, const string& value5,
+ const char* variable6, const string& value6,
+ const char* variable7, const string& value7);
+ // Like the first Print(), except the substitutions are given as parameters.
+ void Print(const char* text, const char* variable1, const string& value1,
+ const char* variable2, const string& value2,
+ const char* variable3, const string& value3,
+ const char* variable4, const string& value4,
+ const char* variable5, const string& value5,
+ const char* variable6, const string& value6,
+ const char* variable7, const string& value7,
+ const char* variable8, const string& value8);
// Indent text by two spaces. After calling Indent(), two spaces will be
// inserted at the beginning of each line of text. Indent() may be called