| // Protocol Buffers - Google's data interchange format | |
| // Copyright 2008 Google Inc. All rights reserved. | |
| // | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file or at | |
| // https://developers.google.com/open-source/licenses/bsd | |
| #include "google/protobuf/parse_context.h" | |
| #include <algorithm> | |
| #include <climits> | |
| #include <cstddef> | |
| #include <cstdint> | |
| #include <cstring> | |
| #include <memory> | |
| #include <string> | |
| #include <utility> | |
| #include "absl/base/optimization.h" | |
| #include "absl/base/prefetch.h" | |
| #include "absl/log/absl_check.h" | |
| #include "absl/numeric/bits.h" | |
| #include "absl/strings/cord.h" | |
| #include "absl/strings/str_cat.h" | |
| #include "absl/strings/string_view.h" | |
| #include "absl/types/span.h" | |
| #include "google/protobuf/internal_visibility.h" | |
| #include "google/protobuf/message_lite.h" | |
| #include "google/protobuf/micro_string.h" | |
| #include "google/protobuf/port.h" | |
| #include "google/protobuf/repeated_field.h" | |
| #include "google/protobuf/wire_format_lite.h" | |
| #include "utf8_validity.h" | |
| // Must be included last. | |
| #include "google/protobuf/port_def.inc" | |
| namespace google { | |
| namespace protobuf { | |
| namespace internal { | |
| namespace { | |
| bool ParsingEndsInBuffer(const char* ptr, const char* end, int depth) { | |
| while (ptr < end) { | |
| uint32_t tag; | |
| ptr = ReadTag(ptr, &tag); | |
| if (ptr == nullptr || ptr > end) return false; | |
| // ending on 0 tag is allowed and is the major reason for the necessity of | |
| // this function. | |
| if (tag == 0) return true; | |
| switch (tag & 7) { | |
| case 0: { // Varint | |
| uint64_t val; | |
| ptr = VarintParse(ptr, &val); | |
| if (ptr == nullptr) return false; | |
| break; | |
| } | |
| case 1: { // fixed64 | |
| ptr += 8; | |
| break; | |
| } | |
| case 2: { // len delim | |
| int32_t size = ReadSize(&ptr); | |
| if (ptr == nullptr || size > end - ptr) return false; | |
| ptr += size; | |
| break; | |
| } | |
| case 3: { // start group | |
| depth++; | |
| break; | |
| } | |
| case 4: { // end group | |
| if (--depth < 0) return true; // We exit early | |
| break; | |
| } | |
| case 5: { // fixed32 | |
| ptr += 4; | |
| break; | |
| } | |
| default: | |
| return false; // Unknown wireformat | |
| } | |
| } | |
| return false; | |
| } | |
| } // namespace | |
| bool EpsCopyInputStream::IsRequestedLessThanOrEqualTo(int requested, | |
| int available) { | |
| return static_cast<int64_t>(static_cast<uint32_t>(requested)) <= | |
| static_cast<int64_t>(available); | |
| } | |
| bool EpsCopyInputStream::CanReadFromPtr(int requested, const char* ptr) { | |
| return IsRequestedLessThanOrEqualTo(requested, BytesAvailable(ptr)); | |
| } | |
| bool EpsCopyInputStream::HasEnoughTillLimit(int requested, const char* ptr) { | |
| return IsRequestedLessThanOrEqualTo(requested, BytesUntilLimit(ptr)); | |
| } | |
| // Only call if at start of tag. | |
| bool EpsCopyInputStream::ParseEndsInSlopRegion(const char* begin, int overrun, | |
| int depth) { | |
| constexpr int kSlopBytes = EpsCopyInputStream::kSlopBytes; | |
| ABSL_DCHECK_GE(overrun, 0); | |
| ABSL_DCHECK_LE(overrun, kSlopBytes); | |
| auto ptr = begin + overrun; | |
| auto end = begin + kSlopBytes; | |
| return ParsingEndsInBuffer(ptr, end, depth); | |
| } | |
| const char* EpsCopyInputStream::NextBuffer(int overrun, int depth) { | |
| if (next_chunk_ == nullptr) return nullptr; // We've reached end of stream. | |
| if (next_chunk_ != patch_buffer_) { | |
| ABSL_DCHECK(size_ > kSlopBytes); | |
| // The chunk is large enough to be used directly | |
| buffer_end_ = next_chunk_ + size_ - kSlopBytes; | |
| auto res = next_chunk_; | |
| next_chunk_ = patch_buffer_; | |
| if (aliasing_ == kOnPatch) aliasing_ = kNoDelta; | |
| return res; | |
| } | |
| // Move the slop bytes of previous buffer to start of the patch buffer. | |
| // Note we must use memmove because the previous buffer could be part of | |
| // patch_buffer_. | |
| std::memmove(patch_buffer_, buffer_end_, kSlopBytes); | |
| if (overall_limit_ > 0 && | |
| (depth < 0 || !ParseEndsInSlopRegion(patch_buffer_, overrun, depth))) { | |
| const void* data; | |
| // ZeroCopyInputStream indicates Next may return 0 size buffers. Hence | |
| // we loop. | |
| while (StreamNext(&data)) { | |
| if (size_ > kSlopBytes) { | |
| // We got a large chunk | |
| std::memcpy(patch_buffer_ + kSlopBytes, data, kSlopBytes); | |
| next_chunk_ = static_cast<const char*>(data); | |
| buffer_end_ = patch_buffer_ + kSlopBytes; | |
| if (aliasing_ >= kNoDelta) aliasing_ = kOnPatch; | |
| return patch_buffer_; | |
| } else if (size_ > 0) { | |
| std::memcpy(patch_buffer_ + kSlopBytes, data, size_); | |
| next_chunk_ = patch_buffer_; | |
| buffer_end_ = patch_buffer_ + size_; | |
| if (aliasing_ >= kNoDelta) aliasing_ = kOnPatch; | |
| return patch_buffer_; | |
| } | |
| ABSL_DCHECK(size_ == 0) << size_; | |
| } | |
| overall_limit_ = 0; // Next failed, no more needs for next | |
| } | |
| // End of stream or array | |
| if (aliasing_ == kNoDelta) { | |
| // If there is no more block and aliasing is true, the previous block | |
| // is still valid and we can alias. We have users relying on string_view's | |
| // obtained from protos to outlive the proto, when the parse was from an | |
| // array. This guarantees string_view's are always aliased if parsed from | |
| // an array. | |
| aliasing_ = reinterpret_cast<std::uintptr_t>(buffer_end_) - | |
| reinterpret_cast<std::uintptr_t>(patch_buffer_); | |
| } | |
| next_chunk_ = nullptr; | |
| buffer_end_ = patch_buffer_ + kSlopBytes; | |
| size_ = 0; | |
| return patch_buffer_; | |
| } | |
| const char* EpsCopyInputStream::Next() { | |
| ABSL_DCHECK(limit_ > kSlopBytes); | |
| auto p = NextBuffer(0 /* immaterial */, -1); | |
| if (p == nullptr) { | |
| limit_end_ = buffer_end_; | |
| // Distinguish ending on a pushed limit or ending on end-of-stream. | |
| SetEndOfStream(); | |
| return nullptr; | |
| } | |
| limit_ -= buffer_end_ - p; // Adjust limit_ relative to new anchor | |
| limit_end_ = buffer_end_ + std::min(0, limit_); | |
| return p; | |
| } | |
| std::pair<const char*, bool> EpsCopyInputStream::DoneFallback(int overrun, | |
| int depth) { | |
| // Did we exceeded the limit (parse error). | |
| if (ABSL_PREDICT_FALSE(overrun > limit_)) return {nullptr, true}; | |
| ABSL_DCHECK(overrun != limit_); // Guaranteed by caller. | |
| ABSL_DCHECK(overrun < limit_); // Follows from above | |
| // TODO Instead of this dcheck we could just assign, and remove | |
| // updating the limit_end from PopLimit, ie. | |
| // limit_end_ = buffer_end_ + (std::min)(0, limit_); | |
| // if (ptr < limit_end_) return {ptr, false}; | |
| ABSL_DCHECK(limit_end_ == buffer_end_ + (std::min)(0, limit_)); | |
| // At this point we know the following assertion holds. | |
| ABSL_DCHECK_GT(limit_, 0); | |
| ABSL_DCHECK(limit_end_ == buffer_end_); // because limit_ > 0 | |
| const char* p; | |
| do { | |
| // We are past the end of buffer_end_, in the slop region. | |
| ABSL_DCHECK_GE(overrun, 0); | |
| p = NextBuffer(overrun, depth); | |
| if (p == nullptr) { | |
| // We are at the end of the stream | |
| if (ABSL_PREDICT_FALSE(overrun != 0)) return {nullptr, true}; | |
| ABSL_DCHECK_GT(limit_, 0); | |
| limit_end_ = buffer_end_; | |
| // Distinguish ending on a pushed limit or ending on end-of-stream. | |
| SetEndOfStream(); | |
| return {buffer_end_, true}; | |
| } | |
| limit_ -= buffer_end_ - p; // Adjust limit_ relative to new anchor | |
| p += overrun; | |
| overrun = p - buffer_end_; | |
| } while (overrun >= 0); | |
| limit_end_ = buffer_end_ + std::min(0, limit_); | |
| return {p, false}; | |
| } | |
| const char* EpsCopyInputStream::SkipFallback(const char* ptr, int size) { | |
| return AppendSize(ptr, size, [](const char* /*p*/, int /*s*/) {}); | |
| } | |
| const char* EpsCopyInputStream::ReadStringFallback(const char* ptr, int size, | |
| std::string* str) { | |
| str->clear(); | |
| if (ABSL_PREDICT_TRUE(HasEnoughTillLimit(size, ptr))) { | |
| // Reserve the string up to a static safe size. If strings are bigger than | |
| // this we proceed by growing the string as needed. This protects against | |
| // malicious payloads making protobuf hold on to a lot of memory. | |
| str->reserve(str->size() + std::min<int>(size, kSafeStringSize)); | |
| } | |
| return AppendSize(ptr, size, | |
| [str](const char* p, int s) { str->append(p, s); }); | |
| } | |
| const char* EpsCopyInputStream::ReadArray(const char* ptr, | |
| absl::Span<char> out) { | |
| if (CanReadFromPtr(out.size(), ptr)) { | |
| memcpy(out.data(), ptr, out.size()); | |
| return ptr + out.size(); | |
| } | |
| return ReadArrayFallback(ptr, out); | |
| } | |
| const char* EpsCopyInputStream::ReadArrayFallback(const char* ptr, | |
| absl::Span<char> out) { | |
| char* dst = out.data(); | |
| return AppendSize(ptr, out.size(), [&dst](const char* p, int s) { | |
| memcpy(dst, p, s); | |
| dst += s; | |
| }); | |
| } | |
| namespace { | |
| // A valid UTF8 ranges in [1, 4]: https://en.wikipedia.org/wiki/UTF-8 | |
| constexpr size_t kUtfMax = 4; | |
| void UnrolledMemcpy(char* dst, const char* src, size_t size) { | |
| PROTOBUF_ASSUME(size < 4); | |
| for (size_t i = 0; i < size; ++i) { | |
| dst[i] = src[i]; | |
| } | |
| } | |
| struct LeftoverBuffer { | |
| absl::string_view view() const { return {buffer, size}; } | |
| bool empty() const { return size == 0; } | |
| void assign(absl::string_view from); | |
| void append(absl::string_view from); | |
| // Removes [0, pos) and moves data to the front if any. | |
| void remove_prefix(uint32_t pos); | |
| uint32_t size = 0; | |
| char buffer[kUtfMax]; | |
| }; | |
| void LeftoverBuffer::assign(absl::string_view from) { | |
| ABSL_DCHECK_LT(from.size(), kUtfMax); | |
| UnrolledMemcpy(buffer, from.data(), from.size()); | |
| size = from.size(); | |
| } | |
| void LeftoverBuffer::append(absl::string_view from) { | |
| ABSL_DCHECK_LE(size + from.size(), kUtfMax); | |
| UnrolledMemcpy(buffer + size, from.data(), from.size()); | |
| size += from.size(); | |
| } | |
| void LeftoverBuffer::remove_prefix(uint32_t pos) { | |
| ABSL_DCHECK_LE(pos, size); | |
| size -= pos; | |
| UnrolledMemcpy(buffer, buffer + pos, size); | |
| } | |
| // Returns true if "fragment" potentially prefixed with "leftover" is valid | |
| // UTF8. Copied from `CordIsValid()`: | |
| // http://google3/util/utf8/internal/unilib.cc;l=85;rcl=740640507 | |
| bool IsViewValidUTF8WithLeftover(absl::string_view fragment, | |
| LeftoverBuffer& leftover) { | |
| if (size_t leftover_size = leftover.size; leftover_size > 0) { | |
| ABSL_DCHECK_LT(leftover_size, kUtfMax); | |
| // Copy into the leftover buffer until it has kUtfMax bytes, and match code | |
| // points within that buffer, removing them from the prefix of the next | |
| // chunk. | |
| const size_t fill_size = kUtfMax - leftover_size; | |
| if (fragment.size() < fill_size) { | |
| // If the full fragment fits in the buffer, match and consume if possible. | |
| leftover.append(fragment); | |
| // Opportunistically validate but it's okay otherwise as we may be | |
| // building up to a valid UTF8. | |
| leftover.remove_prefix( | |
| utf8_range::SpanStructurallyValid(leftover.view())); | |
| return true; | |
| } | |
| // Otherwise, fill the buffer from the prefix of the fragment, match, and | |
| // remove the bytes in the *match* (not the unmatched part of the buffer) | |
| // that originally came from the current fragment. | |
| // | |
| // Note that fragment is big enough to fill leftover to the max UTF8 value. | |
| // It has to have a valid UTF8. | |
| leftover.append(fragment.substr(0, fill_size)); | |
| const size_t leftover_valid = | |
| utf8_range::SpanStructurallyValid(leftover.view()); | |
| if (leftover_valid == 0) { | |
| return false; | |
| } | |
| fragment.remove_prefix(leftover_valid - leftover_size); | |
| } | |
| const size_t valid = utf8_range::SpanStructurallyValid(fragment); | |
| fragment.remove_prefix(valid); | |
| // If the last Unicode char crosses to next fragment, length must be smaller | |
| // than kUtfMax. | |
| if (kUtfMax <= fragment.size()) { | |
| return false; | |
| } | |
| leftover.assign(fragment); | |
| // So far so good. Continue to the next fragment. | |
| return true; | |
| } | |
| } // namespace | |
| const char* EpsCopyInputStream::VerifyUTF8(const char* ptr, size_t size) { | |
| if (size <= static_cast<size_t>(BytesAvailable(ptr))) { | |
| return utf8_range::IsStructurallyValid({ptr, size}) ? ptr + size : nullptr; | |
| } | |
| return VerifyUTF8Fallback(ptr, size); | |
| } | |
| const char* EpsCopyInputStream::VerifyUTF8Fallback(const char* ptr, | |
| size_t size) { | |
| // Copied the implementation of CordIsValid(). | |
| LeftoverBuffer leftover; | |
| ptr = AppendSize(ptr, size, [&leftover](const char* p, int s) -> bool { | |
| return IsViewValidUTF8WithLeftover({p, static_cast<size_t>(s)}, leftover); | |
| }); | |
| return leftover.empty() ? ptr : nullptr; | |
| } | |
| const char* EpsCopyInputStream::AppendStringFallback(const char* ptr, int size, | |
| std::string* str) { | |
| if (ABSL_PREDICT_TRUE( | |
| IsRequestedLessThanOrEqualTo(size, BytesUntilLimit(ptr)))) { | |
| // Reserve the string up to a static safe size. If strings are bigger than | |
| // this we proceed by growing the string as needed. This protects against | |
| // malicious payloads making protobuf hold on to a lot of memory. | |
| str->reserve(str->size() + std::min<int>(size, kSafeStringSize)); | |
| } | |
| return AppendSize(ptr, size, | |
| [str](const char* p, int s) { str->append(p, s); }); | |
| } | |
| const char* EpsCopyInputStream::ReadCordFallback(const char* ptr, int size, | |
| absl::Cord* cord) { | |
| if (zcis_ == nullptr) { | |
| if (CanReadFromPtr(size, ptr)) { | |
| *cord = absl::string_view(ptr, size); | |
| return ptr + size; | |
| } | |
| return AppendSize(ptr, size, [cord](const char* p, int s) { | |
| cord->Append(absl::string_view(p, s)); | |
| }); | |
| } | |
| int new_limit = buffer_end_ - ptr + limit_; | |
| if (!IsRequestedLessThanOrEqualTo(size, new_limit)) return nullptr; | |
| new_limit -= size; | |
| int bytes_from_buffer = BytesAvailable(ptr); | |
| const bool in_patch_buf = reinterpret_cast<uintptr_t>(ptr) - | |
| reinterpret_cast<uintptr_t>(patch_buffer_) <= | |
| kPatchBufferSize; | |
| if (bytes_from_buffer > kPatchBufferSize || !in_patch_buf) { | |
| cord->Clear(); | |
| StreamBackUp(bytes_from_buffer); | |
| } else if (bytes_from_buffer == kSlopBytes && next_chunk_ != nullptr && | |
| // Only backup if next_chunk_ points to a valid buffer returned by | |
| // ZeroCopyInputStream. This happens when NextStream() returns a | |
| // chunk that's smaller than or equal to kSlopBytes. | |
| next_chunk_ != patch_buffer_) { | |
| cord->Clear(); | |
| StreamBackUp(size_); | |
| } else { | |
| size -= bytes_from_buffer; | |
| ABSL_DCHECK_GT(size, 0); | |
| *cord = absl::string_view(ptr, bytes_from_buffer); | |
| if (next_chunk_ == patch_buffer_) { | |
| // We have read to end of the last buffer returned by | |
| // ZeroCopyInputStream. So the stream is in the right position. | |
| } else if (next_chunk_ == nullptr) { | |
| // There is no remaining chunks. We can't read size. | |
| SetEndOfStream(); | |
| return nullptr; | |
| } else { | |
| // Next chunk is already loaded | |
| ABSL_DCHECK(size_ > kSlopBytes); | |
| StreamBackUp(size_ - kSlopBytes); | |
| } | |
| } | |
| if (size > overall_limit_) return nullptr; | |
| overall_limit_ -= size; | |
| if (!zcis_->ReadCord(cord, size)) return nullptr; | |
| ptr = InitFrom(zcis_); | |
| limit_ = new_limit - static_cast<int>(buffer_end_ - ptr); | |
| limit_end_ = buffer_end_ + (std::min)(0, limit_); | |
| return ptr; | |
| } | |
| const char* EpsCopyInputStream::InitFrom(io::ZeroCopyInputStream* zcis) { | |
| zcis_ = zcis; | |
| const void* data; | |
| int size; | |
| limit_ = INT_MAX; | |
| if (zcis->Next(&data, &size)) { | |
| overall_limit_ -= size; | |
| if (size > kSlopBytes) { | |
| auto ptr = static_cast<const char*>(data); | |
| limit_ -= size - kSlopBytes; | |
| limit_end_ = buffer_end_ = ptr + size - kSlopBytes; | |
| next_chunk_ = patch_buffer_; | |
| if (aliasing_ == kOnPatch) aliasing_ = kNoDelta; | |
| return ptr; | |
| } else { | |
| limit_end_ = buffer_end_ = patch_buffer_ + kSlopBytes; | |
| next_chunk_ = patch_buffer_; | |
| auto ptr = patch_buffer_ + kPatchBufferSize - size; | |
| std::memcpy(ptr, data, size); | |
| return ptr; | |
| } | |
| } | |
| overall_limit_ = 0; | |
| next_chunk_ = nullptr; | |
| size_ = 0; | |
| limit_end_ = buffer_end_ = patch_buffer_; | |
| return patch_buffer_; | |
| } | |
| const char* ParseContext::ReadSizeAndPushLimitAndDepth(const char* ptr, | |
| LimitToken* old_limit) { | |
| return ReadSizeAndPushLimitAndDepthInlined(ptr, old_limit); | |
| } | |
| const char* ParseContext::ParseMessage(MessageLite* msg, const char* ptr) { | |
| LimitToken old; | |
| ptr = ReadSizeAndPushLimitAndDepth(ptr, &old); | |
| if (ptr == nullptr) return ptr; | |
| auto old_depth = depth_; | |
| ptr = msg->_InternalParse(ptr, this); | |
| if (ptr != nullptr) ABSL_DCHECK_EQ(old_depth, depth_); | |
| depth_++; | |
| if (!PopLimit(std::move(old))) return nullptr; | |
| return ptr; | |
| } | |
| inline void WriteVarint(uint64_t val, std::string* s) { | |
| while (val >= 128) { | |
| uint8_t c = val | 0x80; | |
| s->push_back(c); | |
| val >>= 7; | |
| } | |
| s->push_back(val); | |
| } | |
| void WriteVarint(uint32_t num, uint64_t val, std::string* s) { | |
| WriteVarint(num << 3, s); | |
| WriteVarint(val, s); | |
| } | |
| void WriteLengthDelimited(uint32_t num, absl::string_view val, std::string* s) { | |
| WriteVarint((num << 3) + 2, s); | |
| WriteVarint(val.size(), s); | |
| s->append(val.data(), val.size()); | |
| } | |
| std::pair<const char*, uint32_t> VarintParseSlow32(const char* p, | |
| uint32_t res) { | |
| for (std::uint32_t i = 1; i < 5; i++) { | |
| uint32_t byte = static_cast<uint8_t>(p[i]); | |
| res += (byte - 1) << (7 * i); | |
| if (ABSL_PREDICT_TRUE(byte < 128)) { | |
| return {p + i + 1, res}; | |
| } | |
| } | |
| // Accept >5 bytes | |
| for (std::uint32_t i = 5; i < 10; i++) { | |
| uint32_t byte = static_cast<uint8_t>(p[i]); | |
| if (ABSL_PREDICT_TRUE(byte < 128)) { | |
| return {p + i + 1, res}; | |
| } | |
| } | |
| return {nullptr, 0}; | |
| } | |
| std::pair<const char*, uint64_t> VarintParseSlow64(const char* p, | |
| uint32_t res32) { | |
| uint64_t res = res32; | |
| for (std::uint32_t i = 1; i < 10; i++) { | |
| uint64_t byte = static_cast<uint8_t>(p[i]); | |
| res += (byte - 1) << (7 * i); | |
| if (ABSL_PREDICT_TRUE(byte < 128)) { | |
| return {p + i + 1, res}; | |
| } | |
| } | |
| return {nullptr, 0}; | |
| } | |
| std::pair<const char*, uint32_t> ReadTagFallback(const char* p, uint32_t res) { | |
| for (std::uint32_t i = 2; i < 5; i++) { | |
| uint32_t byte = static_cast<uint8_t>(p[i]); | |
| res += (byte - 1) << (7 * i); | |
| if (ABSL_PREDICT_TRUE(byte < 128)) { | |
| return {p + i + 1, res}; | |
| } | |
| } | |
| return {nullptr, 0}; | |
| } | |
| std::pair<const char*, int32_t> ReadSizeFallback(const char* p, uint32_t res) { | |
| // Both byte 0 and byte 1 are guaranteed to have bit 7 set upon | |
| // fallback entry. | |
| ABSL_DCHECK(static_cast<uint8_t>(p[0]) >= 128); | |
| ABSL_DCHECK(static_cast<uint8_t>(p[1]) >= 128); | |
| res += (static_cast<uint8_t>(p[1]) - 1) << 7; | |
| for (std::uint32_t i = 2; i < 4; i++) { | |
| uint32_t byte = static_cast<uint8_t>(p[i]); | |
| res += (byte - 1) << (7 * i); | |
| if (ABSL_PREDICT_TRUE(byte < 128)) { | |
| return {p + i + 1, res}; | |
| } | |
| } | |
| std::uint32_t byte = static_cast<uint8_t>(p[4]); | |
| if (ABSL_PREDICT_FALSE(byte >= 8)) return {nullptr, 0}; // size >= 2gb | |
| res += (byte - 1) << 28; | |
| // Protect against sign integer overflow in PushLimit. Limits are relative | |
| // to buffer ends and ptr could potential be kSlopBytes beyond a buffer end. | |
| // To protect against overflow we reject limits absurdly close to INT_MAX. | |
| if (ABSL_PREDICT_FALSE(res > INT_MAX - ParseContext::kSlopBytes)) { | |
| return {nullptr, 0}; | |
| } | |
| return {p + 5, res}; | |
| } | |
| const char* StringParser(const char* begin, const char* end, void* object, | |
| ParseContext*) { | |
| auto str = static_cast<std::string*>(object); | |
| str->append(begin, end - begin); | |
| return end; | |
| } | |
| // Defined in wire_format_lite.cc | |
| void PrintUTF8ErrorLog(absl::string_view message_name, | |
| absl::string_view field_name, const char* operation_str, | |
| bool emit_stacktrace); | |
| bool VerifyUTF8(absl::string_view str, const char* field_name) { | |
| if (!utf8_range::IsStructurallyValid(str)) { | |
| PrintUTF8ErrorLog("", field_name, "parsing", false); | |
| return false; | |
| } | |
| return true; | |
| } | |
| const char* InlineGreedyStringParser(std::string* s, const char* ptr, | |
| ParseContext* ctx) { | |
| int size = ReadSize(&ptr); | |
| if (!ptr) return nullptr; | |
| return ctx->ReadString(ptr, size, s); | |
| } | |
| void WireFormatStringSink::Flush(const char* ptr) { | |
| ABSL_CHECK_GE(ptr, prev); | |
| absl::StrAppend(&data, | |
| absl::string_view(prev, static_cast<size_t>(ptr - prev))); | |
| } | |
| void WireFormatStringSink::Append(absl::string_view view) { | |
| absl::StrAppend(&data, view); | |
| prev = view.data() + view.size(); | |
| } | |
| template <typename SinkT> | |
| [[nodiscard]] const char* EpsCopyInputStream::ReadArrayMaybeFlush( | |
| const char* ptr, absl::Span<char> out, SinkT& sink) { | |
| char* dst = out.data(); | |
| return AdvancePtrMaybeFlush<char>( | |
| ptr, out.size(), sink, [&](absl::string_view view) { | |
| memcpy(dst, view.data(), view.size()); | |
| dst += view.size(); | |
| ABSL_DCHECK_LE(dst, out.data() + out.size()); | |
| return true; | |
| }); | |
| } | |
| template <typename SinkT> | |
| const char* ParseContext::VerifyUTF8MaybeFlushFallback(const char* ptr, | |
| int64_t size, | |
| SinkT& sink) { | |
| // Copied the implementation of CordIsValid(). | |
| LeftoverBuffer leftover; | |
| ptr = AdvancePtrMaybeFlush<char>( | |
| ptr, size, sink, [&leftover](absl::string_view view) -> bool { | |
| return IsViewValidUTF8WithLeftover(view, leftover); | |
| }); | |
| return leftover.empty() ? ptr : nullptr; | |
| } | |
| template const char* EpsCopyInputStream::ReadArrayMaybeFlush( | |
| const char* ptr, absl::Span<char> out, WireFormatNoOpSink& sink); | |
| template const char* EpsCopyInputStream::ReadArrayMaybeFlush( | |
| const char* ptr, absl::Span<char> out, WireFormatStringSink& sink); | |
| template const char* ParseContext::VerifyUTF8MaybeFlushFallback( | |
| const char* ptr, int64_t size, WireFormatNoOpSink& sink); | |
| template const char* ParseContext::VerifyUTF8MaybeFlushFallback( | |
| const char* ptr, int64_t size, WireFormatStringSink& sink); | |
| template <typename T, bool sign> | |
| inline const char* VarintParser(const MessageLite& parent, void* object, | |
| const char* ptr, ParseContext* ctx) { | |
| return ctx->ReadPackedVarint(ptr, [&parent, object](uint64_t varint) { | |
| T val; | |
| if (sign) { | |
| if (sizeof(T) == 8) { | |
| val = WireFormatLite::ZigZagDecode64(varint); | |
| } else { | |
| val = WireFormatLite::ZigZagDecode32(varint); | |
| } | |
| } else { | |
| val = varint; | |
| } | |
| static_cast<RepeatedField<T>*>(object)->InternalAddWithArena( | |
| InternalVisibility(), &parent, val); | |
| }); | |
| } | |
| const char* PackedInt32Parser(const MessageLite& parent, void* object, | |
| const char* ptr, ParseContext* ctx) { | |
| return VarintParser<int32_t, false>(parent, object, ptr, ctx); | |
| } | |
| const char* PackedUInt32Parser(const MessageLite& parent, void* object, | |
| const char* ptr, ParseContext* ctx) { | |
| return VarintParser<uint32_t, false>(parent, object, ptr, ctx); | |
| } | |
| const char* PackedInt64Parser(const MessageLite& parent, void* object, | |
| const char* ptr, ParseContext* ctx) { | |
| return VarintParser<int64_t, false>(parent, object, ptr, ctx); | |
| } | |
| const char* PackedUInt64Parser(const MessageLite& parent, void* object, | |
| const char* ptr, ParseContext* ctx) { | |
| return VarintParser<uint64_t, false>(parent, object, ptr, ctx); | |
| } | |
| const char* PackedSInt32Parser(const MessageLite& parent, void* object, | |
| const char* ptr, ParseContext* ctx) { | |
| return VarintParser<int32_t, true>(parent, object, ptr, ctx); | |
| } | |
| const char* PackedSInt64Parser(const MessageLite& parent, void* object, | |
| const char* ptr, ParseContext* ctx) { | |
| return VarintParser<int64_t, true>(parent, object, ptr, ctx); | |
| } | |
| const char* PackedEnumParser(const MessageLite& parent, void* object, | |
| const char* ptr, ParseContext* ctx) { | |
| return VarintParser<int, false>(parent, object, ptr, ctx); | |
| } | |
| const char* PackedBoolParser(const MessageLite& parent, void* object, | |
| const char* ptr, ParseContext* ctx) { | |
| return VarintParser<bool, false>(parent, object, ptr, ctx); | |
| } | |
| template <typename T> | |
| inline const char* FixedParser(const MessageLite& parent, void* object, | |
| const char* ptr, ParseContext* ctx) { | |
| int size = ReadSize(&ptr); | |
| return ctx->ReadPackedFixed(parent, ptr, size, | |
| static_cast<RepeatedField<T>*>(object)); | |
| } | |
| const char* PackedFixed32Parser(const MessageLite& parent, void* object, | |
| const char* ptr, ParseContext* ctx) { | |
| return FixedParser<uint32_t>(parent, object, ptr, ctx); | |
| } | |
| const char* PackedSFixed32Parser(const MessageLite& parent, void* object, | |
| const char* ptr, ParseContext* ctx) { | |
| return FixedParser<int32_t>(parent, object, ptr, ctx); | |
| } | |
| const char* PackedFixed64Parser(const MessageLite& parent, void* object, | |
| const char* ptr, ParseContext* ctx) { | |
| return FixedParser<uint64_t>(parent, object, ptr, ctx); | |
| } | |
| const char* PackedSFixed64Parser(const MessageLite& parent, void* object, | |
| const char* ptr, ParseContext* ctx) { | |
| return FixedParser<int64_t>(parent, object, ptr, ctx); | |
| } | |
| const char* PackedFloatParser(const MessageLite& parent, void* object, | |
| const char* ptr, ParseContext* ctx) { | |
| return FixedParser<float>(parent, object, ptr, ctx); | |
| } | |
| const char* PackedDoubleParser(const MessageLite& parent, void* object, | |
| const char* ptr, ParseContext* ctx) { | |
| return FixedParser<double>(parent, object, ptr, ctx); | |
| } | |
| class UnknownFieldLiteParserHelper { | |
| public: | |
| explicit UnknownFieldLiteParserHelper(std::string* unknown) | |
| : unknown_(unknown) {} | |
| void AddVarint(uint32_t num, uint64_t value) { | |
| if (unknown_ == nullptr) return; | |
| WriteVarint(num * 8, unknown_); | |
| WriteVarint(value, unknown_); | |
| } | |
| void AddFixed64(uint32_t num, uint64_t value) { | |
| if (unknown_ == nullptr) return; | |
| WriteVarint(num * 8 + 1, unknown_); | |
| char buffer[8]; | |
| // TODO: Remove this suppression. | |
| (void)io::CodedOutputStream::WriteLittleEndian64ToArray( | |
| value, reinterpret_cast<uint8_t*>(buffer)); | |
| unknown_->append(buffer, 8); | |
| } | |
| const char* ParseLengthDelimited(uint32_t num, const char* ptr, | |
| ParseContext* ctx) { | |
| int size = ReadSize(&ptr); | |
| GOOGLE_PROTOBUF_PARSER_ASSERT(ptr); | |
| if (unknown_ == nullptr) return ctx->Skip(ptr, size); | |
| WriteVarint(num * 8 + 2, unknown_); | |
| WriteVarint(size, unknown_); | |
| return ctx->AppendString(ptr, size, unknown_); | |
| } | |
| const char* ParseGroup(uint32_t num, const char* ptr, ParseContext* ctx) { | |
| if (unknown_) WriteVarint(num * 8 + 3, unknown_); | |
| ptr = ctx->ParseGroupInlined(ptr, num * 8 + 3, [&](const char* ptr) { | |
| return WireFormatParser(*this, ptr, ctx); | |
| }); | |
| GOOGLE_PROTOBUF_PARSER_ASSERT(ptr); | |
| if (unknown_) WriteVarint(num * 8 + 4, unknown_); | |
| return ptr; | |
| } | |
| void AddFixed32(uint32_t num, uint32_t value) { | |
| if (unknown_ == nullptr) return; | |
| WriteVarint(num * 8 + 5, unknown_); | |
| char buffer[4]; | |
| // TODO: Remove this suppression. | |
| (void)io::CodedOutputStream::WriteLittleEndian32ToArray( | |
| value, reinterpret_cast<uint8_t*>(buffer)); | |
| unknown_->append(buffer, 4); | |
| } | |
| private: | |
| std::string* unknown_; | |
| }; | |
| const char* UnknownGroupLiteParse(std::string* unknown, const char* ptr, | |
| ParseContext* ctx) { | |
| UnknownFieldLiteParserHelper field_parser(unknown); | |
| return WireFormatParser(field_parser, ptr, ctx); | |
| } | |
| const char* UnknownFieldParse(uint32_t tag, std::string* unknown, | |
| const char* ptr, ParseContext* ctx) { | |
| UnknownFieldLiteParserHelper field_parser(unknown); | |
| return FieldParser(tag, field_parser, ptr, ctx); | |
| } | |
| const char* EpsCopyInputStream::ReadMicroStringFallback( | |
| const char* ptr, int size, MicroString& str, size_t inline_capacity, | |
| const MessageLite& parent) { | |
| str.SetInChunks( | |
| size, parent, | |
| [&](auto append) { | |
| ptr = AppendSize(ptr, size, [&](const char* p, int s) { | |
| append(absl::string_view(p, s)); | |
| }); | |
| }, | |
| inline_capacity); | |
| return ptr; | |
| } | |
| int CountVarintsAssumingLargeArray(const char* ptr, const char* end) { | |
| // The number of varints is the number of bytes with the highest bit clear. | |
| // This is easier to compute as the total number of bytes, minus the number | |
| // of bytes with the highest bit set. | |
| int num_varints = end - ptr; | |
| ABSL_DCHECK_GE(num_varints, int{sizeof(uint64_t)}); | |
| // Count in whole blocks, except for the last one. | |
| const char* const limit = end - sizeof(uint64_t); | |
| while (ptr < limit) { | |
| num_varints -= | |
| absl::popcount(EndianHelper<8>::Load(ptr) & 0x8080808080808080); | |
| ptr += sizeof(uint64_t); | |
| } | |
| // Count in the last, possibly incomplete block. | |
| return num_varints - | |
| absl::popcount(EndianHelper<8>::Load(limit) & | |
| (0x8080808080808080 << ((ptr - limit) * 8))); | |
| } | |
| bool VerifyBoolsAssumingLargeArray(const char* ptr, const char* end) { | |
| ABSL_DCHECK_GE(end - ptr, int{sizeof(uint64_t)}); | |
| // Verify whole blocks, except for the last one. | |
| uint64_t bit_or = 0; | |
| const char* const limit = end - sizeof(uint64_t); | |
| while (ptr < limit) { | |
| uint64_t block; | |
| std::memcpy(&block, ptr, 8); | |
| bit_or |= block; | |
| ptr += 8; | |
| } | |
| // Verify the last, possibly incomplete block. | |
| uint64_t block; | |
| std::memcpy(&block, limit, 8); | |
| bit_or |= block; | |
| return (bit_or & ~0x0101010101010101) == 0; | |
| } | |
| } // namespace internal | |
| } // namespace protobuf | |
| } // namespace google | |
| #include "google/protobuf/port_undef.inc" |