1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc. All rights reserved.
3 // https://developers.google.com/protocol-buffers/
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 // * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 // * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
14 // distribution.
15 // * Neither the name of Google Inc. nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31 #include <google/protobuf/parse_context.h>
32
33 #include <google/protobuf/io/coded_stream.h>
34 #include <google/protobuf/io/zero_copy_stream.h>
35 #include <google/protobuf/arenastring.h>
36 #include <google/protobuf/endian.h>
37 #include <google/protobuf/message_lite.h>
38 #include <google/protobuf/repeated_field.h>
39 #include <google/protobuf/stubs/strutil.h>
40 #include <google/protobuf/wire_format_lite.h>
41
42 // Must be included last.
43 #include <google/protobuf/port_def.inc>
44
45 namespace google {
46 namespace protobuf {
47 namespace internal {
48
49 namespace {
50
51 // Only call if at start of tag.
ParseEndsInSlopRegion(const char * begin,int overrun,int depth)52 bool ParseEndsInSlopRegion(const char* begin, int overrun, int depth) {
53 constexpr int kSlopBytes = EpsCopyInputStream::kSlopBytes;
54 GOOGLE_DCHECK_GE(overrun, 0);
55 GOOGLE_DCHECK_LE(overrun, kSlopBytes);
56 auto ptr = begin + overrun;
57 auto end = begin + kSlopBytes;
58 while (ptr < end) {
59 uint32_t tag;
60 ptr = ReadTag(ptr, &tag);
61 if (ptr == nullptr || ptr > end) return false;
62 // ending on 0 tag is allowed and is the major reason for the necessity of
63 // this function.
64 if (tag == 0) return true;
65 switch (tag & 7) {
66 case 0: { // Varint
67 uint64_t val;
68 ptr = VarintParse(ptr, &val);
69 if (ptr == nullptr) return false;
70 break;
71 }
72 case 1: { // fixed64
73 ptr += 8;
74 break;
75 }
76 case 2: { // len delim
77 int32_t size = ReadSize(&ptr);
78 if (ptr == nullptr || size > end - ptr) return false;
79 ptr += size;
80 break;
81 }
82 case 3: { // start group
83 depth++;
84 break;
85 }
86 case 4: { // end group
87 if (--depth < 0) return true; // We exit early
88 break;
89 }
90 case 5: { // fixed32
91 ptr += 4;
92 break;
93 }
94 default:
95 return false; // Unknown wireformat
96 }
97 }
98 return false;
99 }
100
101 } // namespace
102
NextBuffer(int overrun,int depth)103 const char* EpsCopyInputStream::NextBuffer(int overrun, int depth) {
104 if (next_chunk_ == nullptr) return nullptr; // We've reached end of stream.
105 if (next_chunk_ != buffer_) {
106 GOOGLE_DCHECK(size_ > kSlopBytes);
107 // The chunk is large enough to be used directly
108 buffer_end_ = next_chunk_ + size_ - kSlopBytes;
109 auto res = next_chunk_;
110 next_chunk_ = buffer_;
111 if (aliasing_ == kOnPatch) aliasing_ = kNoDelta;
112 return res;
113 }
114 // Move the slop bytes of previous buffer to start of the patch buffer.
115 // Note we must use memmove because the previous buffer could be part of
116 // buffer_.
117 std::memmove(buffer_, buffer_end_, kSlopBytes);
118 if (overall_limit_ > 0 &&
119 (depth < 0 || !ParseEndsInSlopRegion(buffer_, overrun, depth))) {
120 const void* data;
121 // ZeroCopyInputStream indicates Next may return 0 size buffers. Hence
122 // we loop.
123 while (StreamNext(&data)) {
124 if (size_ > kSlopBytes) {
125 // We got a large chunk
126 std::memcpy(buffer_ + kSlopBytes, data, kSlopBytes);
127 next_chunk_ = static_cast<const char*>(data);
128 buffer_end_ = buffer_ + kSlopBytes;
129 if (aliasing_ >= kNoDelta) aliasing_ = kOnPatch;
130 return buffer_;
131 } else if (size_ > 0) {
132 std::memcpy(buffer_ + kSlopBytes, data, size_);
133 next_chunk_ = buffer_;
134 buffer_end_ = buffer_ + size_;
135 if (aliasing_ >= kNoDelta) aliasing_ = kOnPatch;
136 return buffer_;
137 }
138 GOOGLE_DCHECK(size_ == 0) << size_;
139 }
140 overall_limit_ = 0; // Next failed, no more needs for next
141 }
142 // End of stream or array
143 if (aliasing_ == kNoDelta) {
144 // If there is no more block and aliasing is true, the previous block
145 // is still valid and we can alias. We have users relying on string_view's
146 // obtained from protos to outlive the proto, when the parse was from an
147 // array. This guarantees string_view's are always aliased if parsed from
148 // an array.
149 aliasing_ = reinterpret_cast<std::uintptr_t>(buffer_end_) -
150 reinterpret_cast<std::uintptr_t>(buffer_);
151 }
152 next_chunk_ = nullptr;
153 buffer_end_ = buffer_ + kSlopBytes;
154 size_ = 0;
155 return buffer_;
156 }
157
Next()158 const char* EpsCopyInputStream::Next() {
159 GOOGLE_DCHECK(limit_ > kSlopBytes);
160 auto p = NextBuffer(0 /* immaterial */, -1);
161 if (p == nullptr) {
162 limit_end_ = buffer_end_;
163 // Distinguish ending on a pushed limit or ending on end-of-stream.
164 SetEndOfStream();
165 return nullptr;
166 }
167 limit_ -= buffer_end_ - p; // Adjust limit_ relative to new anchor
168 limit_end_ = buffer_end_ + std::min(0, limit_);
169 return p;
170 }
171
DoneFallback(int overrun,int depth)172 std::pair<const char*, bool> EpsCopyInputStream::DoneFallback(int overrun,
173 int depth) {
174 // Did we exceeded the limit (parse error).
175 if (PROTOBUF_PREDICT_FALSE(overrun > limit_)) return {nullptr, true};
176 GOOGLE_DCHECK(overrun != limit_); // Guaranteed by caller.
177 GOOGLE_DCHECK(overrun < limit_); // Follows from above
178 // TODO(gerbens) Instead of this dcheck we could just assign, and remove
179 // updating the limit_end from PopLimit, ie.
180 // limit_end_ = buffer_end_ + (std::min)(0, limit_);
181 // if (ptr < limit_end_) return {ptr, false};
182 GOOGLE_DCHECK(limit_end_ == buffer_end_ + (std::min)(0, limit_));
183 // At this point we know the following assertion holds.
184 GOOGLE_DCHECK_GT(limit_, 0);
185 GOOGLE_DCHECK(limit_end_ == buffer_end_); // because limit_ > 0
186 const char* p;
187 do {
188 // We are past the end of buffer_end_, in the slop region.
189 GOOGLE_DCHECK_GE(overrun, 0);
190 p = NextBuffer(overrun, depth);
191 if (p == nullptr) {
192 // We are at the end of the stream
193 if (PROTOBUF_PREDICT_FALSE(overrun != 0)) return {nullptr, true};
194 GOOGLE_DCHECK_GT(limit_, 0);
195 limit_end_ = buffer_end_;
196 // Distinguish ending on a pushed limit or ending on end-of-stream.
197 SetEndOfStream();
198 return {buffer_end_, true};
199 }
200 limit_ -= buffer_end_ - p; // Adjust limit_ relative to new anchor
201 p += overrun;
202 overrun = p - buffer_end_;
203 } while (overrun >= 0);
204 limit_end_ = buffer_end_ + std::min(0, limit_);
205 return {p, false};
206 }
207
SkipFallback(const char * ptr,int size)208 const char* EpsCopyInputStream::SkipFallback(const char* ptr, int size) {
209 return AppendSize(ptr, size, [](const char* /*p*/, int /*s*/) {});
210 }
211
ReadStringFallback(const char * ptr,int size,std::string * str)212 const char* EpsCopyInputStream::ReadStringFallback(const char* ptr, int size,
213 std::string* str) {
214 str->clear();
215 if (PROTOBUF_PREDICT_TRUE(size <= buffer_end_ - ptr + limit_)) {
216 // Reserve the string up to a static safe size. If strings are bigger than
217 // this we proceed by growing the string as needed. This protects against
218 // malicious payloads making protobuf hold on to a lot of memory.
219 str->reserve(str->size() + std::min<int>(size, kSafeStringSize));
220 }
221 return AppendSize(ptr, size,
222 [str](const char* p, int s) { str->append(p, s); });
223 }
224
AppendStringFallback(const char * ptr,int size,std::string * str)225 const char* EpsCopyInputStream::AppendStringFallback(const char* ptr, int size,
226 std::string* str) {
227 if (PROTOBUF_PREDICT_TRUE(size <= buffer_end_ - ptr + limit_)) {
228 // Reserve the string up to a static safe size. If strings are bigger than
229 // this we proceed by growing the string as needed. This protects against
230 // malicious payloads making protobuf hold on to a lot of memory.
231 str->reserve(str->size() + std::min<int>(size, kSafeStringSize));
232 }
233 return AppendSize(ptr, size,
234 [str](const char* p, int s) { str->append(p, s); });
235 }
236
237
InitFrom(io::ZeroCopyInputStream * zcis)238 const char* EpsCopyInputStream::InitFrom(io::ZeroCopyInputStream* zcis) {
239 zcis_ = zcis;
240 const void* data;
241 int size;
242 limit_ = INT_MAX;
243 if (zcis->Next(&data, &size)) {
244 overall_limit_ -= size;
245 if (size > kSlopBytes) {
246 auto ptr = static_cast<const char*>(data);
247 limit_ -= size - kSlopBytes;
248 limit_end_ = buffer_end_ = ptr + size - kSlopBytes;
249 next_chunk_ = buffer_;
250 if (aliasing_ == kOnPatch) aliasing_ = kNoDelta;
251 return ptr;
252 } else {
253 limit_end_ = buffer_end_ = buffer_ + kSlopBytes;
254 next_chunk_ = buffer_;
255 auto ptr = buffer_ + 2 * kSlopBytes - size;
256 std::memcpy(ptr, data, size);
257 return ptr;
258 }
259 }
260 overall_limit_ = 0;
261 next_chunk_ = nullptr;
262 size_ = 0;
263 limit_end_ = buffer_end_ = buffer_;
264 return buffer_;
265 }
266
ReadSizeAndPushLimitAndDepth(const char * ptr,int * old_limit)267 const char* ParseContext::ReadSizeAndPushLimitAndDepth(const char* ptr,
268 int* old_limit) {
269 int size = ReadSize(&ptr);
270 if (PROTOBUF_PREDICT_FALSE(!ptr)) {
271 *old_limit = 0; // Make sure this isn't uninitialized even on error return
272 return nullptr;
273 }
274 *old_limit = PushLimit(ptr, size);
275 if (--depth_ < 0) return nullptr;
276 return ptr;
277 }
278
ParseMessage(MessageLite * msg,const char * ptr)279 const char* ParseContext::ParseMessage(MessageLite* msg, const char* ptr) {
280 int old;
281 ptr = ReadSizeAndPushLimitAndDepth(ptr, &old);
282 ptr = ptr ? msg->_InternalParse(ptr, this) : nullptr;
283 depth_++;
284 if (!PopLimit(old)) return nullptr;
285 return ptr;
286 }
287
WriteVarint(uint64_t val,std::string * s)288 inline void WriteVarint(uint64_t val, std::string* s) {
289 while (val >= 128) {
290 uint8_t c = val | 0x80;
291 s->push_back(c);
292 val >>= 7;
293 }
294 s->push_back(val);
295 }
296
WriteVarint(uint32_t num,uint64_t val,std::string * s)297 void WriteVarint(uint32_t num, uint64_t val, std::string* s) {
298 WriteVarint(num << 3, s);
299 WriteVarint(val, s);
300 }
301
WriteLengthDelimited(uint32_t num,StringPiece val,std::string * s)302 void WriteLengthDelimited(uint32_t num, StringPiece val, std::string* s) {
303 WriteVarint((num << 3) + 2, s);
304 WriteVarint(val.size(), s);
305 s->append(val.data(), val.size());
306 }
307
VarintParseSlow32(const char * p,uint32_t res)308 std::pair<const char*, uint32_t> VarintParseSlow32(const char* p,
309 uint32_t res) {
310 for (std::uint32_t i = 2; i < 5; i++) {
311 uint32_t byte = static_cast<uint8_t>(p[i]);
312 res += (byte - 1) << (7 * i);
313 if (PROTOBUF_PREDICT_TRUE(byte < 128)) {
314 return {p + i + 1, res};
315 }
316 }
317 // Accept >5 bytes
318 for (std::uint32_t i = 5; i < 10; i++) {
319 uint32_t byte = static_cast<uint8_t>(p[i]);
320 if (PROTOBUF_PREDICT_TRUE(byte < 128)) {
321 return {p + i + 1, res};
322 }
323 }
324 return {nullptr, 0};
325 }
326
VarintParseSlow64(const char * p,uint32_t res32)327 std::pair<const char*, uint64_t> VarintParseSlow64(const char* p,
328 uint32_t res32) {
329 uint64_t res = res32;
330 for (std::uint32_t i = 2; i < 10; i++) {
331 uint64_t byte = static_cast<uint8_t>(p[i]);
332 res += (byte - 1) << (7 * i);
333 if (PROTOBUF_PREDICT_TRUE(byte < 128)) {
334 return {p + i + 1, res};
335 }
336 }
337 return {nullptr, 0};
338 }
339
ReadTagFallback(const char * p,uint32_t res)340 std::pair<const char*, uint32_t> ReadTagFallback(const char* p, uint32_t res) {
341 for (std::uint32_t i = 2; i < 5; i++) {
342 uint32_t byte = static_cast<uint8_t>(p[i]);
343 res += (byte - 1) << (7 * i);
344 if (PROTOBUF_PREDICT_TRUE(byte < 128)) {
345 return {p + i + 1, res};
346 }
347 }
348 return {nullptr, 0};
349 }
350
ReadSizeFallback(const char * p,uint32_t res)351 std::pair<const char*, int32_t> ReadSizeFallback(const char* p, uint32_t res) {
352 for (std::uint32_t i = 1; i < 4; i++) {
353 uint32_t byte = static_cast<uint8_t>(p[i]);
354 res += (byte - 1) << (7 * i);
355 if (PROTOBUF_PREDICT_TRUE(byte < 128)) {
356 return {p + i + 1, res};
357 }
358 }
359 std::uint32_t byte = static_cast<uint8_t>(p[4]);
360 if (PROTOBUF_PREDICT_FALSE(byte >= 8)) return {nullptr, 0}; // size >= 2gb
361 res += (byte - 1) << 28;
362 // Protect against sign integer overflow in PushLimit. Limits are relative
363 // to buffer ends and ptr could potential be kSlopBytes beyond a buffer end.
364 // To protect against overflow we reject limits absurdly close to INT_MAX.
365 if (PROTOBUF_PREDICT_FALSE(res > INT_MAX - ParseContext::kSlopBytes)) {
366 return {nullptr, 0};
367 }
368 return {p + 5, res};
369 }
370
StringParser(const char * begin,const char * end,void * object,ParseContext *)371 const char* StringParser(const char* begin, const char* end, void* object,
372 ParseContext*) {
373 auto str = static_cast<std::string*>(object);
374 str->append(begin, end - begin);
375 return end;
376 }
377
378 // Defined in wire_format_lite.cc
379 void PrintUTF8ErrorLog(StringPiece message_name,
380 StringPiece field_name, const char* operation_str,
381 bool emit_stacktrace);
382
VerifyUTF8(StringPiece str,const char * field_name)383 bool VerifyUTF8(StringPiece str, const char* field_name) {
384 if (!IsStructurallyValidUTF8(str)) {
385 PrintUTF8ErrorLog("", field_name, "parsing", false);
386 return false;
387 }
388 return true;
389 }
390
InlineGreedyStringParser(std::string * s,const char * ptr,ParseContext * ctx)391 const char* InlineGreedyStringParser(std::string* s, const char* ptr,
392 ParseContext* ctx) {
393 int size = ReadSize(&ptr);
394 if (!ptr) return nullptr;
395 return ctx->ReadString(ptr, size, s);
396 }
397
398
399 template <typename T, bool sign>
VarintParser(void * object,const char * ptr,ParseContext * ctx)400 const char* VarintParser(void* object, const char* ptr, ParseContext* ctx) {
401 return ctx->ReadPackedVarint(ptr, [object](uint64_t varint) {
402 T val;
403 if (sign) {
404 if (sizeof(T) == 8) {
405 val = WireFormatLite::ZigZagDecode64(varint);
406 } else {
407 val = WireFormatLite::ZigZagDecode32(varint);
408 }
409 } else {
410 val = varint;
411 }
412 static_cast<RepeatedField<T>*>(object)->Add(val);
413 });
414 }
415
PackedInt32Parser(void * object,const char * ptr,ParseContext * ctx)416 const char* PackedInt32Parser(void* object, const char* ptr,
417 ParseContext* ctx) {
418 return VarintParser<int32_t, false>(object, ptr, ctx);
419 }
PackedUInt32Parser(void * object,const char * ptr,ParseContext * ctx)420 const char* PackedUInt32Parser(void* object, const char* ptr,
421 ParseContext* ctx) {
422 return VarintParser<uint32_t, false>(object, ptr, ctx);
423 }
PackedInt64Parser(void * object,const char * ptr,ParseContext * ctx)424 const char* PackedInt64Parser(void* object, const char* ptr,
425 ParseContext* ctx) {
426 return VarintParser<int64_t, false>(object, ptr, ctx);
427 }
PackedUInt64Parser(void * object,const char * ptr,ParseContext * ctx)428 const char* PackedUInt64Parser(void* object, const char* ptr,
429 ParseContext* ctx) {
430 return VarintParser<uint64_t, false>(object, ptr, ctx);
431 }
PackedSInt32Parser(void * object,const char * ptr,ParseContext * ctx)432 const char* PackedSInt32Parser(void* object, const char* ptr,
433 ParseContext* ctx) {
434 return VarintParser<int32_t, true>(object, ptr, ctx);
435 }
PackedSInt64Parser(void * object,const char * ptr,ParseContext * ctx)436 const char* PackedSInt64Parser(void* object, const char* ptr,
437 ParseContext* ctx) {
438 return VarintParser<int64_t, true>(object, ptr, ctx);
439 }
440
PackedEnumParser(void * object,const char * ptr,ParseContext * ctx)441 const char* PackedEnumParser(void* object, const char* ptr, ParseContext* ctx) {
442 return VarintParser<int, false>(object, ptr, ctx);
443 }
444
PackedBoolParser(void * object,const char * ptr,ParseContext * ctx)445 const char* PackedBoolParser(void* object, const char* ptr, ParseContext* ctx) {
446 return VarintParser<bool, false>(object, ptr, ctx);
447 }
448
449 template <typename T>
FixedParser(void * object,const char * ptr,ParseContext * ctx)450 const char* FixedParser(void* object, const char* ptr, ParseContext* ctx) {
451 int size = ReadSize(&ptr);
452 return ctx->ReadPackedFixed(ptr, size,
453 static_cast<RepeatedField<T>*>(object));
454 }
455
PackedFixed32Parser(void * object,const char * ptr,ParseContext * ctx)456 const char* PackedFixed32Parser(void* object, const char* ptr,
457 ParseContext* ctx) {
458 return FixedParser<uint32_t>(object, ptr, ctx);
459 }
PackedSFixed32Parser(void * object,const char * ptr,ParseContext * ctx)460 const char* PackedSFixed32Parser(void* object, const char* ptr,
461 ParseContext* ctx) {
462 return FixedParser<int32_t>(object, ptr, ctx);
463 }
PackedFixed64Parser(void * object,const char * ptr,ParseContext * ctx)464 const char* PackedFixed64Parser(void* object, const char* ptr,
465 ParseContext* ctx) {
466 return FixedParser<uint64_t>(object, ptr, ctx);
467 }
PackedSFixed64Parser(void * object,const char * ptr,ParseContext * ctx)468 const char* PackedSFixed64Parser(void* object, const char* ptr,
469 ParseContext* ctx) {
470 return FixedParser<int64_t>(object, ptr, ctx);
471 }
PackedFloatParser(void * object,const char * ptr,ParseContext * ctx)472 const char* PackedFloatParser(void* object, const char* ptr,
473 ParseContext* ctx) {
474 return FixedParser<float>(object, ptr, ctx);
475 }
PackedDoubleParser(void * object,const char * ptr,ParseContext * ctx)476 const char* PackedDoubleParser(void* object, const char* ptr,
477 ParseContext* ctx) {
478 return FixedParser<double>(object, ptr, ctx);
479 }
480
481 class UnknownFieldLiteParserHelper {
482 public:
UnknownFieldLiteParserHelper(std::string * unknown)483 explicit UnknownFieldLiteParserHelper(std::string* unknown)
484 : unknown_(unknown) {}
485
AddVarint(uint32_t num,uint64_t value)486 void AddVarint(uint32_t num, uint64_t value) {
487 if (unknown_ == nullptr) return;
488 WriteVarint(num * 8, unknown_);
489 WriteVarint(value, unknown_);
490 }
AddFixed64(uint32_t num,uint64_t value)491 void AddFixed64(uint32_t num, uint64_t value) {
492 if (unknown_ == nullptr) return;
493 WriteVarint(num * 8 + 1, unknown_);
494 char buffer[8];
495 io::CodedOutputStream::WriteLittleEndian64ToArray(
496 value, reinterpret_cast<uint8_t*>(buffer));
497 unknown_->append(buffer, 8);
498 }
ParseLengthDelimited(uint32_t num,const char * ptr,ParseContext * ctx)499 const char* ParseLengthDelimited(uint32_t num, const char* ptr,
500 ParseContext* ctx) {
501 int size = ReadSize(&ptr);
502 GOOGLE_PROTOBUF_PARSER_ASSERT(ptr);
503 if (unknown_ == nullptr) return ctx->Skip(ptr, size);
504 WriteVarint(num * 8 + 2, unknown_);
505 WriteVarint(size, unknown_);
506 return ctx->AppendString(ptr, size, unknown_);
507 }
ParseGroup(uint32_t num,const char * ptr,ParseContext * ctx)508 const char* ParseGroup(uint32_t num, const char* ptr, ParseContext* ctx) {
509 if (unknown_) WriteVarint(num * 8 + 3, unknown_);
510 ptr = ctx->ParseGroup(this, ptr, num * 8 + 3);
511 GOOGLE_PROTOBUF_PARSER_ASSERT(ptr);
512 if (unknown_) WriteVarint(num * 8 + 4, unknown_);
513 return ptr;
514 }
AddFixed32(uint32_t num,uint32_t value)515 void AddFixed32(uint32_t num, uint32_t value) {
516 if (unknown_ == nullptr) return;
517 WriteVarint(num * 8 + 5, unknown_);
518 char buffer[4];
519 io::CodedOutputStream::WriteLittleEndian32ToArray(
520 value, reinterpret_cast<uint8_t*>(buffer));
521 unknown_->append(buffer, 4);
522 }
523
_InternalParse(const char * ptr,ParseContext * ctx)524 const char* _InternalParse(const char* ptr, ParseContext* ctx) {
525 return WireFormatParser(*this, ptr, ctx);
526 }
527
528 private:
529 std::string* unknown_;
530 };
531
UnknownGroupLiteParse(std::string * unknown,const char * ptr,ParseContext * ctx)532 const char* UnknownGroupLiteParse(std::string* unknown, const char* ptr,
533 ParseContext* ctx) {
534 UnknownFieldLiteParserHelper field_parser(unknown);
535 return WireFormatParser(field_parser, ptr, ctx);
536 }
537
UnknownFieldParse(uint32_t tag,std::string * unknown,const char * ptr,ParseContext * ctx)538 const char* UnknownFieldParse(uint32_t tag, std::string* unknown,
539 const char* ptr, ParseContext* ctx) {
540 UnknownFieldLiteParserHelper field_parser(unknown);
541 return FieldParser(tag, field_parser, ptr, ctx);
542 }
543
544 } // namespace internal
545 } // namespace protobuf
546 } // namespace google
547
548 #include <google/protobuf/port_undef.inc>
549