@@ -260,6 +260,11 @@ class PROTOBUF_EXPORT EpsCopyInputStream {
260260 template <typename Add, typename SizeCb>
261261 [[nodiscard]] const char * ReadPackedVarint (const char * ptr, Add add,
262262 SizeCb size_callback);
263+ // Same as above, but pass the field directly , so we can preallocate.
264+ template <typename Convert, typename T>
265+ [[nodiscard]] const char * ReadPackedVarintWithField (const char * ptr,
266+ Convert conv,
267+ RepeatedField<T>& out);
263268
264269 uint32_t LastTag () const { return last_tag_minus_1_ + 1 ; }
265270 bool ConsumeEndGroup (uint32_t start_tag) {
@@ -1362,6 +1367,83 @@ const char* ReadPackedVarintArray(const char* ptr, const char* end, Add add) {
13621367 return ptr;
13631368}
13641369
1370+ template <typename Convert, typename T>
1371+ const char * ReadPackedVarintArrayWithField (const char * ptr, const char * end,
1372+ Convert conv,
1373+ RepeatedField<T>& out) {
1374+ // If we have enough bytes, we will spend more cpu cycles growing repeated
1375+ // field, than parsing, so count the number of ints first and preallocate.
1376+ // Assume that varint are valid and just count the number of bytes with
1377+ // continuation bit not set. In a valid varint there is only 1 such byte.
1378+ if ((end - ptr) >= 16 && (out.Capacity () - out.size () < end - ptr)) {
1379+ int old_size = out.size ();
1380+ int count = out.Capacity () - out.size ();
1381+ // We are not guaranteed to have enough space for worst possible case,
1382+ // do an actual count and reserve.
1383+ if (count < end - ptr) {
1384+ count = std::count_if (ptr, end, [](char c) { return (c & 0x80 ) == 0 ; });
1385+ // We can overread, so if the last byte has a continuation bit set,
1386+ // we need to account for that.
1387+ if (end[-1 ] & 0x80 ) count++;
1388+ out.Reserve (old_size + count);
1389+ }
1390+ T* x = out.AddNAlreadyReserved (count);
1391+ ptr = ReadPackedVarintArray (ptr, end, [&](uint64_t varint) {
1392+ *x = conv (varint);
1393+ x++;
1394+ });
1395+ int new_size = x - out.data ();
1396+ ABSL_DCHECK_LE (new_size, old_size + count);
1397+ // We may have overreserved if there was enough capacitiy.
1398+ // Or encountered malformed data, so set the actaul size to
1399+ // avoid exposing uninitialized memory.
1400+ out.Truncate (new_size);
1401+ return ptr;
1402+ } else {
1403+ return ReadPackedVarintArray (
1404+ ptr, end, [&](uint64_t varint) { out.Add (conv (varint)); });
1405+ }
1406+ }
1407+
1408+ template <typename Convert, typename T>
1409+ const char * EpsCopyInputStream::ReadPackedVarintWithField (
1410+ const char * ptr, Convert conv, RepeatedField<T>& out) {
1411+ int size = ReadSize (&ptr);
1412+
1413+ GOOGLE_PROTOBUF_PARSER_ASSERT (ptr);
1414+ int chunk_size = static_cast <int >(buffer_end_ - ptr);
1415+ while (size > chunk_size) {
1416+ ptr = ReadPackedVarintArrayWithField (ptr, buffer_end_, conv, out);
1417+ if (ptr == nullptr ) return nullptr ;
1418+ int overrun = static_cast <int >(ptr - buffer_end_);
1419+ ABSL_DCHECK (overrun >= 0 && overrun <= kSlopBytes );
1420+ if (size - chunk_size <= kSlopBytes ) {
1421+ // The current buffer contains all the information needed, we don't need
1422+ // to flip buffers. However we must parse from a buffer with enough space
1423+ // so we are not prone to a buffer overflow.
1424+ char buf[kSlopBytes + 10 ] = {};
1425+ std::memcpy (buf, buffer_end_, kSlopBytes );
1426+ ABSL_CHECK_LE (size - chunk_size, kSlopBytes );
1427+ auto end = buf + (size - chunk_size);
1428+ auto result = ReadPackedVarintArray (
1429+ buf + overrun, end, [&](uint64_t varint) { out.Add (conv (varint)); });
1430+ if (result == nullptr || result != end) return nullptr ;
1431+ return buffer_end_ + (result - buf);
1432+ }
1433+ size -= overrun + chunk_size;
1434+ ABSL_DCHECK_GT (size, 0 );
1435+ // We must flip buffers
1436+ if (limit_ <= kSlopBytes ) return nullptr ;
1437+ ptr = Next ();
1438+ if (ptr == nullptr ) return nullptr ;
1439+ ptr += overrun;
1440+ chunk_size = static_cast <int >(buffer_end_ - ptr);
1441+ }
1442+ auto end = ptr + size;
1443+ ptr = ReadPackedVarintArrayWithField (ptr, end, conv, out);
1444+ return end == ptr ? ptr : nullptr ;
1445+ }
1446+
13651447template <typename Add, typename SizeCb>
13661448const char * EpsCopyInputStream::ReadPackedVarint (const char * ptr, Add add,
13671449 SizeCb size_callback) {
0 commit comments