Spaces:
Sleeping
Sleeping
Plan2Align-NV
/
laser
/tools-external
/sentencepiece-master
/third_party
/protobuf-lite
/coded_stream.cc
// Protocol Buffers - Google's data interchange format | |
// Copyright 2008 Google Inc. All rights reserved. | |
// https://developers.google.com/protocol-buffers/ | |
// | |
// 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. | |
// Author: [email protected] (Kenton Varda) | |
// Based on original Protocol Buffers design by | |
// Sanjay Ghemawat, Jeff Dean, and others. | |
// | |
// This implementation is heavily optimized to make reads and writes | |
// of small values (especially varints) as fast as possible. In | |
// particular, we optimize for the common case that a read or a write | |
// will not cross the end of the buffer, since we can avoid a lot | |
// of branching in this case. | |
namespace google { | |
namespace protobuf { | |
namespace io { | |
namespace { | |
static const int kMaxVarintBytes = 10; | |
static const int kMaxVarint32Bytes = 5; | |
inline bool NextNonEmpty(ZeroCopyInputStream* input, const void** data, | |
int* size) { | |
bool success; | |
do { | |
success = input->Next(data, size); | |
} while (success && *size == 0); | |
return success; | |
} | |
} // namespace | |
// CodedInputStream ================================================== | |
CodedInputStream::~CodedInputStream() { | |
if (input_ != NULL) { | |
BackUpInputToCurrentPosition(); | |
} | |
} | |
// Static. | |
int CodedInputStream::default_recursion_limit_ = 100; | |
void CodedInputStream::BackUpInputToCurrentPosition() { | |
int backup_bytes = BufferSize() + buffer_size_after_limit_ + overflow_bytes_; | |
if (backup_bytes > 0) { | |
input_->BackUp(backup_bytes); | |
// total_bytes_read_ doesn't include overflow_bytes_. | |
total_bytes_read_ -= BufferSize() + buffer_size_after_limit_; | |
buffer_end_ = buffer_; | |
buffer_size_after_limit_ = 0; | |
overflow_bytes_ = 0; | |
} | |
} | |
inline void CodedInputStream::RecomputeBufferLimits() { | |
buffer_end_ += buffer_size_after_limit_; | |
int closest_limit = std::min(current_limit_, total_bytes_limit_); | |
if (closest_limit < total_bytes_read_) { | |
// The limit position is in the current buffer. We must adjust | |
// the buffer size accordingly. | |
buffer_size_after_limit_ = total_bytes_read_ - closest_limit; | |
buffer_end_ -= buffer_size_after_limit_; | |
} else { | |
buffer_size_after_limit_ = 0; | |
} | |
} | |
CodedInputStream::Limit CodedInputStream::PushLimit(int byte_limit) { | |
// Current position relative to the beginning of the stream. | |
int current_position = CurrentPosition(); | |
Limit old_limit = current_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 (PROTOBUF_PREDICT_TRUE(byte_limit >= 0 && | |
byte_limit <= INT_MAX - current_position && | |
byte_limit < current_limit_ - current_position)) { | |
current_limit_ = current_position + byte_limit; | |
RecomputeBufferLimits(); | |
} | |
return old_limit; | |
} | |
void CodedInputStream::PopLimit(Limit limit) { | |
// The limit passed in is actually the *old* limit, which we returned from | |
// PushLimit(). | |
current_limit_ = limit; | |
RecomputeBufferLimits(); | |
// We may no longer be at a legitimate message end. ReadTag() needs to be | |
// called again to find out. | |
legitimate_message_end_ = false; | |
} | |
std::pair<CodedInputStream::Limit, int> | |
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); | |
GOOGLE_DCHECK_LT(recursion_budget_, recursion_limit_); | |
++recursion_budget_; | |
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(); | |
return current_limit_ - current_position; | |
} | |
void CodedInputStream::SetTotalBytesLimit(int total_bytes_limit) { | |
// Make sure the limit isn't already past, since this could confuse other | |
// code. | |
int current_position = CurrentPosition(); | |
total_bytes_limit_ = std::max(current_position, total_bytes_limit); | |
RecomputeBufferLimits(); | |
} | |
int CodedInputStream::BytesUntilTotalBytesLimit() const { | |
if (total_bytes_limit_ == INT_MAX) return -1; | |
return total_bytes_limit_ - CurrentPosition(); | |
} | |
void CodedInputStream::PrintTotalBytesLimitError() { | |
GOOGLE_LOG(ERROR) | |
<< "A protocol message was rejected because it was too " | |
"big (more than " | |
<< total_bytes_limit_ | |
<< " bytes). To increase the limit (or to disable these " | |
"warnings), see CodedInputStream::SetTotalBytesLimit() " | |
"in third_party/protobuf/src/google/protobuf/io/coded_stream.h."; | |
} | |
bool CodedInputStream::SkipFallback(int count, int original_buffer_size) { | |
if (buffer_size_after_limit_ > 0) { | |
// We hit a limit inside this buffer. Advance to the limit and fail. | |
Advance(original_buffer_size); | |
return false; | |
} | |
count -= original_buffer_size; | |
buffer_ = NULL; | |
buffer_end_ = buffer_; | |
// Make sure this skip doesn't try to skip past the current limit. | |
int closest_limit = std::min(current_limit_, total_bytes_limit_); | |
int bytes_until_limit = closest_limit - total_bytes_read_; | |
if (bytes_until_limit < count) { | |
// We hit the limit. Skip up to it then fail. | |
if (bytes_until_limit > 0) { | |
total_bytes_read_ = closest_limit; | |
input_->Skip(bytes_until_limit); | |
} | |
return false; | |
} | |
if (!input_->Skip(count)) { | |
total_bytes_read_ = input_->ByteCount(); | |
return false; | |
} | |
total_bytes_read_ += count; | |
return true; | |
} | |
bool CodedInputStream::GetDirectBufferPointer(const void** data, int* size) { | |
if (BufferSize() == 0 && !Refresh()) return false; | |
*data = buffer_; | |
*size = BufferSize(); | |
return true; | |
} | |
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; | |
} | |
bool CodedInputStream::ReadString(std::string* buffer, int size) { | |
if (size < 0) return false; // security: size is often user-supplied | |
if (BufferSize() >= size) { | |
STLStringResizeUninitialized(buffer, size); | |
std::pair<char*, bool> z = as_string_data(buffer); | |
if (z.second) { | |
// Oddly enough, memcpy() requires its first two args to be non-NULL even | |
// if we copy 0 bytes. So, we have ensured that z.first is non-NULL here. | |
GOOGLE_DCHECK(z.first != NULL); | |
memcpy(z.first, buffer_, size); | |
Advance(size); | |
} | |
return true; | |
} | |
return ReadStringFallback(buffer, size); | |
} | |
bool CodedInputStream::ReadStringFallback(std::string* buffer, int size) { | |
if (!buffer->empty()) { | |
buffer->clear(); | |
} | |
int closest_limit = std::min(current_limit_, total_bytes_limit_); | |
if (closest_limit != INT_MAX) { | |
int bytes_to_limit = closest_limit - CurrentPosition(); | |
if (bytes_to_limit > 0 && size > 0 && size <= bytes_to_limit) { | |
buffer->reserve(size); | |
} | |
} | |
int current_buffer_size; | |
while ((current_buffer_size = BufferSize()) < size) { | |
// Some STL implementations "helpfully" crash on buffer->append(NULL, 0). | |
if (current_buffer_size != 0) { | |
// Note: string1.append(string2) is O(string2.size()) (as opposed to | |
// O(string1.size() + string2.size()), which would be bad). | |
buffer->append(reinterpret_cast<const char*>(buffer_), | |
current_buffer_size); | |
} | |
size -= current_buffer_size; | |
Advance(current_buffer_size); | |
if (!Refresh()) return false; | |
} | |
buffer->append(reinterpret_cast<const char*>(buffer_), size); | |
Advance(size); | |
return true; | |
} | |
bool CodedInputStream::ReadLittleEndian32Fallback(uint32* value) { | |
uint8 bytes[sizeof(*value)]; | |
const uint8* ptr; | |
if (BufferSize() >= sizeof(*value)) { | |
// Fast path: Enough bytes in the buffer to read directly. | |
ptr = buffer_; | |
Advance(sizeof(*value)); | |
} else { | |
// Slow path: Had to read past the end of the buffer. | |
if (!ReadRaw(bytes, sizeof(*value))) return false; | |
ptr = bytes; | |
} | |
ReadLittleEndian32FromArray(ptr, value); | |
return true; | |
} | |
bool CodedInputStream::ReadLittleEndian64Fallback(uint64* value) { | |
uint8 bytes[sizeof(*value)]; | |
const uint8* ptr; | |
if (BufferSize() >= sizeof(*value)) { | |
// Fast path: Enough bytes in the buffer to read directly. | |
ptr = buffer_; | |
Advance(sizeof(*value)); | |
} else { | |
// Slow path: Had to read past the end of the buffer. | |
if (!ReadRaw(bytes, sizeof(*value))) return false; | |
ptr = bytes; | |
} | |
ReadLittleEndian64FromArray(ptr, value); | |
return true; | |
} | |
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. | |
PROTOBUF_ALWAYS_INLINE | |
::std::pair<bool, const uint8*> ReadVarint32FromArray(uint32 first_byte, | |
const uint8* buffer, | |
uint32* value); | |
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 = 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; | |
result -= 0x80 << 14; | |
b = *(ptr++); | |
result += b << 21; | |
if (!(b & 0x80)) goto done; | |
result -= 0x80 << 21; | |
b = *(ptr++); | |
result += b << 28; | |
if (!(b & 0x80)) goto done; | |
// "result -= 0x80 << 28" is irrevelant. | |
// If the input is larger than 32 bits, we still need to read it all | |
// and discard the high-order bits. | |
for (int i = 0; i < kMaxVarintBytes - kMaxVarint32Bytes; i++) { | |
b = *(ptr++); | |
if (!(b & 0x80)) goto done; | |
} | |
// We have overrun the maximum size of a varint (10 bytes). Assume | |
// the data is corrupt. | |
return std::make_pair(false, ptr); | |
done: | |
*value = result; | |
return std::make_pair(true, ptr); | |
} | |
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) { | |
// 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); | |
} | |
return std::make_pair(true, next); | |
} | |
} // namespace | |
bool CodedInputStream::ReadVarint32Slow(uint32* value) { | |
// Directly invoke ReadVarint64Fallback, since we already tried to optimize | |
// for one-byte varints. | |
std::pair<uint64, bool> p = ReadVarint64Fallback(); | |
*value = static_cast<uint32>(p.first); | |
return p.second; | |
} | |
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))) { | |
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% | |
uint32 temp; | |
return ReadVarint32Slow(&temp) ? static_cast<int64>(temp) : -1; | |
} | |
} | |
int CodedInputStream::ReadVarintSizeAsIntSlow() { | |
// Directly invoke ReadVarint64Fallback, since we already tried to optimize | |
// for one-byte varints. | |
std::pair<uint64, bool> p = ReadVarint64Fallback(); | |
if (!p.second || p.first > static_cast<uint64>(INT_MAX)) return -1; | |
return p.first; | |
} | |
int CodedInputStream::ReadVarintSizeAsIntFallback() { | |
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))) { | |
uint64 temp; | |
::std::pair<bool, const uint8*> p = ReadVarint64FromArray(buffer_, &temp); | |
if (!p.first || temp > static_cast<uint64>(INT_MAX)) 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 ReadVarintSizeAsIntSlow(); | |
} | |
} | |
uint32 CodedInputStream::ReadTagSlow() { | |
if (buffer_ == buffer_end_) { | |
// Call refresh. | |
if (!Refresh()) { | |
// Refresh failed. Make sure that it failed due to EOF, not because | |
// we hit total_bytes_limit_, which, unlike normal limits, is not a | |
// valid place to end a message. | |
int current_position = total_bytes_read_ - buffer_size_after_limit_; | |
if (current_position >= total_bytes_limit_) { | |
// Hit total_bytes_limit_. But if we also hit the normal limit, | |
// we're still OK. | |
legitimate_message_end_ = current_limit_ == total_bytes_limit_; | |
} else { | |
legitimate_message_end_ = true; | |
} | |
return 0; | |
} | |
} | |
// For the slow path, just do a 64-bit read. Try to optimize for one-byte tags | |
// again, since we have now refreshed the buffer. | |
uint64 result = 0; | |
if (!ReadVarint64(&result)) return 0; | |
return static_cast<uint32>(result); | |
} | |
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; | |
::std::pair<bool, const uint8*> p = | |
ReadVarint32FromArray(first_byte_or_zero, buffer_, &tag); | |
if (!p.first) { | |
return 0; | |
} | |
buffer_ = p.second; | |
return tag; | |
} else { | |
// We are commonly at a limit when attempting to read tags. Try to quickly | |
// detect this case without making another function call. | |
if ((buf_size == 0) && | |
((buffer_size_after_limit_ > 0) || | |
(total_bytes_read_ == current_limit_)) && | |
// Make sure that the limit we hit is not total_bytes_limit_, since | |
// in that case we still need to call Refresh() so that it prints an | |
// error. | |
total_bytes_read_ - buffer_size_after_limit_ < total_bytes_limit_) { | |
// We hit a byte limit. | |
legitimate_message_end_ = true; | |
return 0; | |
} | |
return ReadTagSlow(); | |
} | |
} | |
bool CodedInputStream::ReadVarint64Slow(uint64* value) { | |
// Slow path: This read might cross the end of the buffer, so we | |
// need to check and refresh the buffer if and when it does. | |
uint64 result = 0; | |
int count = 0; | |
uint32 b; | |
do { | |
if (count == kMaxVarintBytes) { | |
*value = 0; | |
return false; | |
} | |
while (buffer_ == buffer_end_) { | |
if (!Refresh()) { | |
*value = 0; | |
return false; | |
} | |
} | |
b = *buffer_; | |
result |= static_cast<uint64>(b & 0x7F) << (7 * count); | |
Advance(1); | |
++count; | |
} while (b & 0x80); | |
*value = result; | |
return true; | |
} | |
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. | |
(buffer_end_ > buffer_ && !(buffer_end_[-1] & 0x80))) { | |
uint64 temp; | |
::std::pair<bool, const uint8*> p = ReadVarint64FromArray(buffer_, &temp); | |
if (!p.first) { | |
return std::make_pair(0, false); | |
} | |
buffer_ = p.second; | |
return std::make_pair(temp, true); | |
} else { | |
uint64 temp; | |
bool success = ReadVarint64Slow(&temp); | |
return std::make_pair(temp, success); | |
} | |
} | |
bool CodedInputStream::Refresh() { | |
GOOGLE_DCHECK_EQ(0, BufferSize()); | |
if (buffer_size_after_limit_ > 0 || overflow_bytes_ > 0 || | |
total_bytes_read_ == current_limit_) { | |
// We've hit a limit. Stop. | |
int current_position = total_bytes_read_ - buffer_size_after_limit_; | |
if (current_position >= total_bytes_limit_ && | |
total_bytes_limit_ != current_limit_) { | |
// Hit total_bytes_limit_. | |
PrintTotalBytesLimitError(); | |
} | |
return false; | |
} | |
const void* void_buffer; | |
int buffer_size; | |
if (NextNonEmpty(input_, &void_buffer, &buffer_size)) { | |
buffer_ = reinterpret_cast<const uint8*>(void_buffer); | |
buffer_end_ = buffer_ + buffer_size; | |
GOOGLE_CHECK_GE(buffer_size, 0); | |
if (total_bytes_read_ <= INT_MAX - buffer_size) { | |
total_bytes_read_ += buffer_size; | |
} else { | |
// Overflow. Reset buffer_end_ to not include the bytes beyond INT_MAX. | |
// We can't get that far anyway, because total_bytes_limit_ is guaranteed | |
// to be less than it. We need to keep track of the number of bytes | |
// we discarded, though, so that we can call input_->BackUp() to back | |
// up over them on destruction. | |
// The following line is equivalent to: | |
// overflow_bytes_ = total_bytes_read_ + buffer_size - INT_MAX; | |
// except that it avoids overflows. Signed integer overflow has | |
// undefined results according to the C standard. | |
overflow_bytes_ = total_bytes_read_ - (INT_MAX - buffer_size); | |
buffer_end_ -= overflow_bytes_; | |
total_bytes_read_ = INT_MAX; | |
} | |
RecomputeBufferLimits(); | |
return true; | |
} else { | |
buffer_ = NULL; | |
buffer_end_ = NULL; | |
return false; | |
} | |
} | |
// CodedOutputStream ================================================= | |
void EpsCopyOutputStream::EnableAliasing(bool enabled) { | |
aliasing_enabled_ = enabled && stream_->AllowsAliasing(); | |
} | |
int64 EpsCopyOutputStream::ByteCount(uint8* ptr) const { | |
// Calculate the current offset relative to the end of the stream buffer. | |
int delta = (end_ - ptr) + (buffer_end_ ? 0 : kSlopBytes); | |
return stream_->ByteCount() - delta; | |
} | |
// Flushes what's written out to the underlying ZeroCopyOutputStream buffers. | |
// Returns the size remaining in the buffer and sets buffer_end_ to the start | |
// of the remaining buffer, ie. [buffer_end_, buffer_end_ + return value) | |
int EpsCopyOutputStream::Flush(uint8* ptr) { | |
while (buffer_end_ && ptr > end_) { | |
int overrun = ptr - end_; | |
GOOGLE_DCHECK(!had_error_); | |
GOOGLE_DCHECK(overrun <= kSlopBytes); // NOLINT | |
ptr = Next() + overrun; | |
if (had_error_) return 0; | |
} | |
int s; | |
if (buffer_end_) { | |
std::memcpy(buffer_end_, buffer_, ptr - buffer_); | |
buffer_end_ += ptr - buffer_; | |
s = end_ - ptr; | |
} else { | |
// The stream is writing directly in the ZeroCopyOutputStream buffer. | |
s = end_ + kSlopBytes - ptr; | |
buffer_end_ = ptr; | |
} | |
GOOGLE_DCHECK(s >= 0); // NOLINT | |
return s; | |
} | |
uint8* EpsCopyOutputStream::Trim(uint8* ptr) { | |
if (had_error_) return ptr; | |
int s = Flush(ptr); | |
if (s) stream_->BackUp(s); | |
// Reset to initial state (expecting new buffer) | |
buffer_end_ = end_ = buffer_; | |
return buffer_; | |
} | |
uint8* EpsCopyOutputStream::FlushAndResetBuffer(uint8* ptr) { | |
if (had_error_) return buffer_; | |
int s = Flush(ptr); | |
if (had_error_) return buffer_; | |
return SetInitialBuffer(buffer_end_, s); | |
} | |
bool EpsCopyOutputStream::Skip(int count, uint8** pp) { | |
if (count < 0) return false; | |
if (had_error_) { | |
*pp = buffer_; | |
return false; | |
} | |
int size = Flush(*pp); | |
if (had_error_) { | |
*pp = buffer_; | |
return false; | |
} | |
void* data = buffer_end_; | |
while (count > size) { | |
count -= size; | |
if (!stream_->Next(&data, &size)) { | |
*pp = Error(); | |
return false; | |
} | |
} | |
*pp = SetInitialBuffer(static_cast<uint8*>(data) + count, size - count); | |
return true; | |
} | |
bool EpsCopyOutputStream::GetDirectBufferPointer(void** data, int* size, | |
uint8** pp) { | |
if (had_error_) { | |
*pp = buffer_; | |
return false; | |
} | |
*size = Flush(*pp); | |
if (had_error_) { | |
*pp = buffer_; | |
return false; | |
} | |
*data = buffer_end_; | |
while (*size == 0) { | |
if (!stream_->Next(data, size)) { | |
*pp = Error(); | |
return false; | |
} | |
} | |
*pp = SetInitialBuffer(*data, *size); | |
return true; | |
} | |
uint8* EpsCopyOutputStream::GetDirectBufferForNBytesAndAdvance(int size, | |
uint8** pp) { | |
if (had_error_) { | |
*pp = buffer_; | |
return nullptr; | |
} | |
int s = Flush(*pp); | |
if (had_error_) { | |
*pp = buffer_; | |
return nullptr; | |
} | |
if (s >= size) { | |
auto res = buffer_end_; | |
*pp = SetInitialBuffer(buffer_end_ + size, s - size); | |
return res; | |
} else { | |
*pp = SetInitialBuffer(buffer_end_, s); | |
return nullptr; | |
} | |
} | |
uint8* EpsCopyOutputStream::Next() { | |
GOOGLE_DCHECK(!had_error_); // NOLINT | |
if (PROTOBUF_PREDICT_FALSE(stream_ == nullptr)) return Error(); | |
if (buffer_end_) { | |
// We're in the patch buffer and need to fill up the previous buffer. | |
std::memcpy(buffer_end_, buffer_, end_ - buffer_); | |
uint8* ptr; | |
int size; | |
do { | |
void* data; | |
if (PROTOBUF_PREDICT_FALSE(!stream_->Next(&data, &size))) { | |
// Stream has an error, we use the patch buffer to continue to be | |
// able to write. | |
return Error(); | |
} | |
ptr = static_cast<uint8*>(data); | |
} while (size == 0); | |
if (PROTOBUF_PREDICT_TRUE(size > kSlopBytes)) { | |
std::memcpy(ptr, end_, kSlopBytes); | |
end_ = ptr + size - kSlopBytes; | |
buffer_end_ = nullptr; | |
return ptr; | |
} else { | |
GOOGLE_DCHECK(size > 0); // NOLINT | |
// Buffer to small | |
std::memmove(buffer_, end_, kSlopBytes); | |
buffer_end_ = ptr; | |
end_ = buffer_ + size; | |
return buffer_; | |
} | |
} else { | |
std::memcpy(buffer_, end_, kSlopBytes); | |
buffer_end_ = end_; | |
end_ = buffer_ + kSlopBytes; | |
return buffer_; | |
} | |
} | |
uint8* EpsCopyOutputStream::EnsureSpaceFallback(uint8* ptr) { | |
do { | |
if (PROTOBUF_PREDICT_FALSE(had_error_)) return buffer_; | |
int overrun = ptr - end_; | |
GOOGLE_DCHECK(overrun >= 0); // NOLINT | |
GOOGLE_DCHECK(overrun <= kSlopBytes); // NOLINT | |
ptr = Next() + overrun; | |
} while (ptr >= end_); | |
GOOGLE_DCHECK(ptr < end_); // NOLINT | |
return ptr; | |
} | |
uint8* EpsCopyOutputStream::WriteRawFallback(const void* data, int size, | |
uint8* ptr) { | |
int s = GetSize(ptr); | |
while (s < size) { | |
std::memcpy(ptr, data, s); | |
size -= s; | |
data = static_cast<const uint8*>(data) + s; | |
ptr = EnsureSpaceFallback(ptr + s); | |
s = GetSize(ptr); | |
} | |
std::memcpy(ptr, data, size); | |
return ptr + size; | |
} | |
uint8* EpsCopyOutputStream::WriteAliasedRaw(const void* data, int size, | |
uint8* ptr) { | |
if (size < GetSize(ptr) | |
) { | |
return WriteRaw(data, size, ptr); | |
} else { | |
ptr = Trim(ptr); | |
if (stream_->WriteAliasedRaw(data, size)) return ptr; | |
return Error(); | |
} | |
} | |
uint8* EpsCopyOutputStream::WriteRawLittleEndian32(const void* data, int size, | |
uint8* ptr) { | |
auto p = static_cast<const uint8*>(data); | |
auto end = p + size; | |
while (end - p >= kSlopBytes) { | |
ptr = EnsureSpace(ptr); | |
uint32 buffer[4]; | |
static_assert(sizeof(buffer) == kSlopBytes, "Buffer must be kSlopBytes"); | |
std::memcpy(buffer, p, kSlopBytes); | |
p += kSlopBytes; | |
for (auto x : buffer) | |
ptr = CodedOutputStream::WriteLittleEndian32ToArray(x, ptr); | |
} | |
while (p < end) { | |
ptr = EnsureSpace(ptr); | |
uint32 buffer; | |
std::memcpy(&buffer, p, 4); | |
p += 4; | |
ptr = CodedOutputStream::WriteLittleEndian32ToArray(buffer, ptr); | |
} | |
return ptr; | |
} | |
uint8* EpsCopyOutputStream::WriteRawLittleEndian64(const void* data, int size, | |
uint8* ptr) { | |
auto p = static_cast<const uint8*>(data); | |
auto end = p + size; | |
while (end - p >= kSlopBytes) { | |
ptr = EnsureSpace(ptr); | |
uint64 buffer[2]; | |
static_assert(sizeof(buffer) == kSlopBytes, "Buffer must be kSlopBytes"); | |
std::memcpy(buffer, p, kSlopBytes); | |
p += kSlopBytes; | |
for (auto x : buffer) | |
ptr = CodedOutputStream::WriteLittleEndian64ToArray(x, ptr); | |
} | |
while (p < end) { | |
ptr = EnsureSpace(ptr); | |
uint64 buffer; | |
std::memcpy(&buffer, p, 8); | |
p += 8; | |
ptr = CodedOutputStream::WriteLittleEndian64ToArray(buffer, ptr); | |
} | |
return ptr; | |
} | |
uint8* EpsCopyOutputStream::WriteStringMaybeAliasedOutline(uint32 num, | |
const std::string& s, | |
uint8* ptr) { | |
ptr = EnsureSpace(ptr); | |
uint32 size = s.size(); | |
ptr = WriteLengthDelim(num, size, ptr); | |
return WriteRawMaybeAliased(s.data(), size, ptr); | |
} | |
uint8* EpsCopyOutputStream::WriteStringOutline(uint32 num, const std::string& s, | |
uint8* ptr) { | |
ptr = EnsureSpace(ptr); | |
uint32 size = s.size(); | |
ptr = WriteLengthDelim(num, size, ptr); | |
return WriteRaw(s.data(), size, ptr); | |
} | |
std::atomic<bool> CodedOutputStream::default_serialization_deterministic_{ | |
false}; | |
CodedOutputStream::CodedOutputStream(ZeroCopyOutputStream* stream, | |
bool do_eager_refresh) | |
: impl_(stream, IsDefaultSerializationDeterministic(), &cur_), | |
start_count_(stream->ByteCount()) { | |
if (do_eager_refresh) { | |
void* data; | |
int size; | |
if (!stream->Next(&data, &size) || size == 0) return; | |
cur_ = impl_.SetInitialBuffer(data, size); | |
} | |
} | |
CodedOutputStream::~CodedOutputStream() { Trim(); } | |
uint8* CodedOutputStream::WriteStringWithSizeToArray(const std::string& str, | |
uint8* target) { | |
GOOGLE_DCHECK_LE(str.size(), kuint32max); | |
target = WriteVarint32ToArray(str.size(), target); | |
return WriteStringToArray(str, target); | |
} | |
} // namespace io | |
} // namespace protobuf | |
} // namespace google | |