Repository: bitcoin/bitcoin
File: src/memusage.h (C/C++)

Code before:
// STL data structures

template<typename X>
struct stl_tree_node
{
private:
    int color;
    void* parent;
    void* left;
    void* right;
    X x;
};

struct stl_shared_counter
{
    /* Various platforms use different sized counters here.
     * Conservatively assume that they won't be larger than size_t. */
    void* class_type;
    size_t use_count;
    size_t weak_count;
};

template<typename X>
static inline size_t DynamicUsage(const std::vector<X>& v)
{
    return MallocUsage(v.capacity() * sizeof(X));
}

template<unsigned int N, typename X, typename S, typename D>
static inline size_t DynamicUsage(const prevector<N, X, S, D>& v)
{
    return MallocUsage(v.allocated_memory());
}

template<typename X, typename Y>
static inline size_t DynamicUsage(const std::set<X, Y>& s)
{
    return MallocUsage(sizeof(stl_tree_node<X>)) * s.size();
}

template<typename X, typename Y>
static inline size_t IncrementalDynamicUsage(const std::set<X, Y>& s)
{
    return MallocUsage(sizeof(stl_tree_node<X>));
}

template<typename X, typename Y, typename Z>
static inline size_t DynamicUsage(const std::map<X, Y, Z>& m)
{
    return MallocUsage(sizeof(stl_tree_node<std::pair<const X, Y> >)) * m.size();
}

Review comment (suggestion):
Slightly unrelated, but `MemUsage` could be generalized to explain the alignment and rounding:

<details>
<summary>Details</summary>

```C++
static inline size_t MallocUsage(const size_t alloc)
{
    if (alloc == 0) return 0;
    constexpr size_t alignment{2 * sizeof(void*)};
    constexpr size_t round_up{alignment * 2 - 1};
    constexpr size_t mask{~(alignment - 1)};
    return alloc + round_up & mask;
}
```
and
```C++
BOOST_AUTO_TEST_CASE(malloc_usage)
{
    auto referenceMallocUsage{[](size_t alloc) -> size_t {
        // Measured on libc6 2.19 on Linux.
        if (alloc == 0) {
            return 0;
        } else if (sizeof(void*) == 8) {
            return ((alloc + 31) >> 4) << 4;
        } else if (sizeof(void*) == 4) {
            return ((alloc + 15) >> 3) << 3;
        } else {
            assert(0);
        }
    }};

    FastRandomContext rng{/*fDeterministic=*/false};
    for (int i{0}; i < 100; ++i) {
        const size_t randomAlloc{rng.randrange(1000)};
        auto expected{referenceMallocUsage(randomAlloc)};
        auto actual{memusage::MallocUsage(randomAlloc)};
        BOOST_CHECK_EQUAL(expected, actual);
    }
}
```

</details>

Code after:

// STL data structures

template<typename X>
struct stl_tree_node
{
private:
    int color;
    void* parent;
    void* left;
    void* right;
    X x;
};

struct stl_shared_counter
{
    /* Various platforms use different sized counters here.
     * Conservatively assume that they won't be larger than size_t. */
    void* class_type;
    size_t use_count;
    size_t weak_count;
};

template<typename T, typename Allocator>
static inline size_t DynamicUsage(const std::vector<T, Allocator>& v)
{
    return MallocUsage(v.capacity() * sizeof(T));
}

static inline size_t DynamicUsage(const std::string& s)
{
    const char* s_ptr = reinterpret_cast<const char*>(&s);
    // Don't count the dynamic memory used for string, if it resides in the
    // "small string" optimization area (which stores data inside the object itself, up to some
    // size; 15 bytes in modern libstdc++).
    if (!std::less{}(s.data(), s_ptr) && !std::greater{}(s.data() + s.size(), s_ptr + sizeof(s))) {
        return 0;
    }
    return MallocUsage(s.capacity());
}

template<unsigned int N, typename X, typename S, typename D>
static inline size_t DynamicUsage(const prevector<N, X, S, D>& v)
{
    return MallocUsage(v.allocated_memory());
}

template<typename X, typename Y>
static inline size_t DynamicUsage(const std::set<X, Y>& s)
{
    return MallocUsage(sizeof(stl_tree_node<X>)) * s.size();