> +
> +/* Read a raw host-endian 32-bit word into VALUE, advancing CUR. Returns
> + false when [CUR, END) is too short. */
> +bool
> +gcov_read_uint32 (const uint8_t *&cur, const uint8_t *end, uint32_t &value)
> +{
> + if (cur > end || sizeof (value) > (size_t) (end - cur))
>= ?
again the boundary checks shouldn't be open coded everywhere.
> +/* Read a length-prefixed string into OUT, dropping the trailing NUL. */
> +bool
> +gcov_read_string (const uint8_t *&cur, const uint8_t *end, std::string &out)
> +{
> + uint32_t byte_len = 0;
> + if (!gcov_read_uint32 (cur, end, byte_len) || byte_len == 0
> + || cur > end || byte_len > (size_t) (end - cur)
> + || cur[byte_len - 1] != '\0')
> + return false;
Why is empty not valid?
> +/* Cumulative profile-count targets in millionths of the total count;
> + for example, 10000 denotes 1 percent. */
> +constexpr uint32_t SUMMARY_CUTOFFS[] = {10000,
> + 100000,
> + 200000,
> + 300000,
> + 400000,
> + 500000,
> + 600000,
> + 700000,
> + 800000,
> + 900000,
> + 950000,
> + 990000,
> + 999000,
> + 999900,
> + 999990,
> + 999999};
I assume the switch to 999 .. . is for compatibility?
Some comment would be good.
> +
> +/* Count -> number of positions with that count. */
> +using count_histogram = std::map<uint64_t, uint64_t, std::greater<uint64_t>>;
Would a hash table be more efficient?
> +accumulate (const gcov_function &f, count_histogram &freq, uint64_t &total,
> + uint64_t &max_count, uint64_t &num_counts)
> +{
> + for (const auto &loc : f.locations)
> + {
> + if (loc->type == gcov_location::kind::inlined)
> + {
> + accumulate (
> + static_cast<const gcov_location_inline *> (loc.get ())->inlined_sym,
> + freq,
> + total,
> + max_count,
> + num_counts);
> + continue;
> + }
> + const uint64_t c = loc->total_count ();
> + if (c == 0)
> + continue;
> + total = gcov_add_count (total, c);
> + if (c > max_count)
> + max_count = c;
> + ++num_counts;
> + ++freq[c];
Some issues with the indentation. also elsewhere.
> +
> + auto it = freq.begin ();
> + const auto end = freq.end ();
> + uint64_t curr_sum = 0, counts_seen = 0, count = 0;
> + for (unsigned i = 0; i < n; ++i)
> + {
> + const uint32_t cutoff = SUMMARY_CUTOFFS[i];
> + const unsigned __int128 desired
> + = (unsigned __int128) total * cutoff / 1000000u;
Is __int128 really needed? This might be unportable, but I suppose
it doesn't really matter right now.
> + while (curr_sum < desired && it != end)
> + {
> + count = it->first;
> + curr_sum = gcov_add_count (curr_sum, it->first * it->second);
> + counts_seen = gcov_add_count (counts_seen, it->second);
I assume overflow is unlikely even for very long profiles, but have you
analyzed this?