diff options
author | Håvard Pettersen <havardpe@yahooinc.com> | 2022-06-09 10:29:01 +0000 |
---|---|---|
committer | Håvard Pettersen <havardpe@yahooinc.com> | 2022-06-10 14:12:30 +0000 |
commit | ce82a9d52c5f1676c2e4795f631269e7992b1877 (patch) | |
tree | d02f0a7cf5aab103d4d6e703617168fa0851e793 /vespalib | |
parent | 0768aded2eeca29027178260875fab85610c2c30 (diff) |
common memory tools
Diffstat (limited to 'vespalib')
-rw-r--r-- | vespalib/src/tests/memory/memory_test.cpp | 234 | ||||
-rw-r--r-- | vespalib/src/vespa/vespalib/util/memory.h | 68 |
2 files changed, 207 insertions, 95 deletions
diff --git a/vespalib/src/tests/memory/memory_test.cpp b/vespalib/src/tests/memory/memory_test.cpp index 4d7c0b1c4d4..1c58da3f663 100644 --- a/vespalib/src/tests/memory/memory_test.cpp +++ b/vespalib/src/tests/memory/memory_test.cpp @@ -19,90 +19,82 @@ public: virtual A * clone() const override { return new A(*this); } }; -class Test : public TestApp -{ -public: - int Main() override; -}; +TEST("require that MallocAutoPtr works as expected") { + MallocAutoPtr a(malloc(30)); + EXPECT_TRUE(a.get() != nullptr); + void * tmp = a.get(); + MallocAutoPtr b(std::move(a)); + EXPECT_TRUE(tmp == b.get()); + EXPECT_TRUE(a.get() == nullptr); + MallocAutoPtr c; + c = std::move(b); + EXPECT_TRUE(b.get() == nullptr); + EXPECT_TRUE(tmp == c.get()); + MallocAutoPtr d(malloc(30)); + EXPECT_TRUE(d.get() != nullptr); + tmp = c.get(); + d = std::move(c); + EXPECT_TRUE(tmp == d.get()); + EXPECT_TRUE(c.get() == nullptr); +} -int -Test::Main() -{ - TEST_INIT("memory_test"); - { - MallocAutoPtr a(malloc(30)); - EXPECT_TRUE(a.get() != nullptr); - void * tmp = a.get(); - MallocAutoPtr b(std::move(a)); - EXPECT_TRUE(tmp == b.get()); - EXPECT_TRUE(a.get() == nullptr); - MallocAutoPtr c; - c = std::move(b); - EXPECT_TRUE(b.get() == nullptr); - EXPECT_TRUE(tmp == c.get()); - MallocAutoPtr d(malloc(30)); - EXPECT_TRUE(d.get() != nullptr); - tmp = c.get(); - d = std::move(c); - EXPECT_TRUE(tmp == d.get()); - EXPECT_TRUE(c.get() == nullptr); - } - { +TEST("require that MallocPtr works as expected") { + MallocPtr a(100); + EXPECT_TRUE(a.size() == 100); + EXPECT_TRUE(a.get() != nullptr); + memset(a.get(), 17, a.size()); + MallocPtr b(a); + EXPECT_TRUE(a.size() == 100); + EXPECT_TRUE(a.get() != nullptr); + EXPECT_TRUE(b.size() == 100); + EXPECT_TRUE(b.get() != nullptr); + EXPECT_TRUE(a.get() != b.get()); + EXPECT_TRUE(memcmp(a.get(), b.get(), a.size()) == 0); + void * tmp = a.get(); + a = b; + EXPECT_TRUE(a.size() == 100); + EXPECT_TRUE(a.get() != nullptr); + EXPECT_TRUE(a.get() != tmp); + EXPECT_TRUE(memcmp(a.get(), b.get(), a.size()) == 0); + MallocPtr d = std::move(b); + EXPECT_TRUE(d.size() == 100); + EXPECT_TRUE(d.get() != nullptr); + EXPECT_TRUE(b.size() == 0); + EXPECT_TRUE(b.get() == nullptr); + MallocPtr c; + c.realloc(89); + EXPECT_EQUAL(c.size(), 89u); + c.realloc(0); + EXPECT_EQUAL(c.size(), 0u); + EXPECT_TRUE(c == nullptr); +} - MallocPtr a(100); - EXPECT_TRUE(a.size() == 100); - EXPECT_TRUE(a.get() != nullptr); - memset(a.get(), 17, a.size()); - MallocPtr b(a); - EXPECT_TRUE(a.size() == 100); - EXPECT_TRUE(a.get() != nullptr); - EXPECT_TRUE(b.size() == 100); - EXPECT_TRUE(b.get() != nullptr); - EXPECT_TRUE(a.get() != b.get()); - EXPECT_TRUE(memcmp(a.get(), b.get(), a.size()) == 0); - void * tmp = a.get(); - a = b; - EXPECT_TRUE(a.size() == 100); - EXPECT_TRUE(a.get() != nullptr); - EXPECT_TRUE(a.get() != tmp); - EXPECT_TRUE(memcmp(a.get(), b.get(), a.size()) == 0); - MallocPtr d = std::move(b); - EXPECT_TRUE(d.size() == 100); - EXPECT_TRUE(d.get() != nullptr); - EXPECT_TRUE(b.size() == 0); - EXPECT_TRUE(b.get() == nullptr); - MallocPtr c; - c.realloc(89); - EXPECT_EQUAL(c.size(), 89u); - c.realloc(0); - EXPECT_EQUAL(c.size(), 0u); - EXPECT_TRUE(c == nullptr); +TEST("require that CloneablePtr works as expected") { + CloneablePtr<B> a(new A()); + EXPECT_TRUE(a.get() != nullptr); + CloneablePtr<B> b(a); + EXPECT_TRUE(a.get() != nullptr); + EXPECT_TRUE(b.get() != nullptr); + EXPECT_TRUE(b.get() != a.get()); + CloneablePtr<B> c; + c = a; + EXPECT_TRUE(a.get() != nullptr); + EXPECT_TRUE(c.get() != nullptr); + EXPECT_TRUE(c.get() != a.get()); - } - { - CloneablePtr<B> a(new A()); - EXPECT_TRUE(a.get() != nullptr); - CloneablePtr<B> b(a); - EXPECT_TRUE(a.get() != nullptr); - EXPECT_TRUE(b.get() != nullptr); - EXPECT_TRUE(b.get() != a.get()); - CloneablePtr<B> c; - c = a; - EXPECT_TRUE(a.get() != nullptr); - EXPECT_TRUE(c.get() != nullptr); - EXPECT_TRUE(c.get() != a.get()); + b = CloneablePtr<B>(new B()); + EXPECT_TRUE(dynamic_cast<B*>(b.get()) != nullptr); + EXPECT_TRUE(dynamic_cast<A*>(b.get()) == nullptr); + EXPECT_TRUE(dynamic_cast<B*>(a.get()) != nullptr); + EXPECT_TRUE(dynamic_cast<A*>(a.get()) != nullptr); + EXPECT_TRUE(dynamic_cast<B*>(c.get()) != nullptr); + EXPECT_TRUE(dynamic_cast<A*>(c.get()) != nullptr); + c = b; + EXPECT_TRUE(dynamic_cast<B*>(c.get()) != nullptr); + EXPECT_TRUE(dynamic_cast<A*>(c.get()) == nullptr); +} - b = CloneablePtr<B>(new B()); - EXPECT_TRUE(dynamic_cast<B*>(b.get()) != nullptr); - EXPECT_TRUE(dynamic_cast<A*>(b.get()) == nullptr); - EXPECT_TRUE(dynamic_cast<B*>(a.get()) != nullptr); - EXPECT_TRUE(dynamic_cast<A*>(a.get()) != nullptr); - EXPECT_TRUE(dynamic_cast<B*>(c.get()) != nullptr); - EXPECT_TRUE(dynamic_cast<A*>(c.get()) != nullptr); - c = b; - EXPECT_TRUE(dynamic_cast<B*>(c.get()) != nullptr); - EXPECT_TRUE(dynamic_cast<A*>(c.get()) == nullptr); - } +TEST("require that CloneablePtr bool conversion works as expected") { { CloneablePtr<B> null; if (null) { @@ -121,17 +113,75 @@ Test::Main() EXPECT_TRUE(false); } } - { - int a[3]; - int b[4] = {0,1,2,3}; - int c[4] = {0,1,2}; - int d[] = {0,1,2,3,4}; - EXPECT_EQUAL(VESPA_NELEMS(a), 3u); - EXPECT_EQUAL(VESPA_NELEMS(b), 4u); - EXPECT_EQUAL(VESPA_NELEMS(c), 4u); - EXPECT_EQUAL(VESPA_NELEMS(d), 5u); - } - TEST_DONE(); } -TEST_APPHOOK(Test) +TEST("require that VESPA_NELEMS works as expected") { + int a[3]; + int b[4] = {0,1,2,3}; + int c[4] = {0,1,2}; + int d[] = {0,1,2,3,4}; + EXPECT_EQUAL(VESPA_NELEMS(a), 3u); + EXPECT_EQUAL(VESPA_NELEMS(b), 4u); + EXPECT_EQUAL(VESPA_NELEMS(c), 4u); + EXPECT_EQUAL(VESPA_NELEMS(d), 5u); +} + +TEST("require that memcpy_safe works as expected") { + vespalib::string a("abcdefgh"); + vespalib::string b("01234567"); + memcpy_safe(&b[0], &a[0], 4); + memcpy_safe(nullptr, &a[0], 0); + memcpy_safe(&b[0], nullptr, 0); + memcpy_safe(nullptr, nullptr, 0); + EXPECT_EQUAL(vespalib::string("abcdefgh"), a); + EXPECT_EQUAL(vespalib::string("abcd4567"), b); +} + +TEST("require that memmove_safe works as expected") { + vespalib::string str("0123456789"); + memmove_safe(&str[2], &str[0], 5); + memmove_safe(nullptr, &str[0], 0); + memmove_safe(&str[0], nullptr, 0); + memmove_safe(nullptr, nullptr, 0); + EXPECT_EQUAL(vespalib::string("0101234789"), str); +} + +TEST("require that memcmp_safe works as expected") { + vespalib::string a("ab"); + vespalib::string b("ac"); + EXPECT_EQUAL(memcmp_safe(&a[0], &b[0], 0), 0); + EXPECT_EQUAL(memcmp_safe(nullptr, &b[0], 0), 0); + EXPECT_EQUAL(memcmp_safe(&a[0], nullptr, 0), 0); + EXPECT_EQUAL(memcmp_safe(nullptr, nullptr, 0), 0); + EXPECT_EQUAL(memcmp_safe(&a[0], &b[0], 1), 0); + EXPECT_LESS(memcmp_safe(&a[0], &b[0], 2), 0); + EXPECT_GREATER(memcmp_safe(&b[0], &a[0], 2), 0); +} + +TEST("require that Unaligned wrapper works as expected") { + struct Data { + char buf[sizeof(uint32_t) * 11]; // space for 10 unaligned values + void *get(size_t idx) { return buf + (idx * sizeof(uint32_t)) + 3; } + const void *cget(size_t idx) { return get(idx); } + Data() { memset(buf, 0, sizeof(buf)); } + }; + Data data; + EXPECT_EQUAL(sizeof(Unaligned<uint32_t>), sizeof(uint32_t)); + EXPECT_EQUAL(alignof(Unaligned<uint32_t>), 1u); + Unaligned<uint32_t> *arr = &Unaligned<uint32_t>::at(data.get(0)); + const Unaligned<uint32_t> *carr = &Unaligned<uint32_t>::at(data.cget(0)); + Unaligned<uint32_t>::at(data.get(0)).write(123); + Unaligned<uint32_t>::at(data.get(1)) = 456; + arr[2] = 789; + arr[3] = arr[0]; + arr[4] = arr[1].read(); + arr[5].write(arr[2]); + EXPECT_EQUAL(Unaligned<uint32_t>::at(data.get(0)).read(), 123u); + EXPECT_EQUAL(Unaligned<uint32_t>::at(data.get(1)), 456u); + EXPECT_EQUAL(arr[2], 789u); + EXPECT_EQUAL(carr[3].read(), 123u); + EXPECT_EQUAL(carr[4], 456u); + EXPECT_EQUAL(carr[5], 789u); +} + +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/vespalib/src/vespa/vespalib/util/memory.h b/vespalib/src/vespa/vespalib/util/memory.h index da164eba0a6..51a89a487e1 100644 --- a/vespalib/src/vespa/vespalib/util/memory.h +++ b/vespalib/src/vespa/vespalib/util/memory.h @@ -11,6 +11,70 @@ namespace vespalib { +inline void *memcpy_safe(void *dest, const void *src, size_t n) noexcept { + if (n > 0) [[likely]] { + memcpy(dest, src, n); + } + return dest; +} + +inline void *memmove_safe(void *dest, const void *src, size_t n) noexcept { + if (n > 0) [[likely]] { + memmove(dest, src, n); + } + return dest; +} + +inline int memcmp_safe(const void *s1, const void *s2, size_t n) noexcept { + if (n == 0) [[unlikely]] { + return 0; + } else [[likely]] { + return memcmp(s1, s2, n); + } +} + +/** + * Wrapper class that enables unaligned access to trivial values. + **/ +template <typename T> +class Unaligned { +private: + char _data[sizeof(T)]; + +public: + Unaligned() = delete; + Unaligned(const Unaligned &) = delete; + Unaligned(Unaligned &&) = delete; + + Unaligned &operator=(const Unaligned &) = default; + Unaligned &operator=(Unaligned &&) = default; + + static_assert(std::is_trivial_v<T>); + static_assert(alignof(T) > 1, "value is always aligned"); + + constexpr static Unaligned &at(void *ptr) noexcept { + return *reinterpret_cast<Unaligned*>(ptr); + } + constexpr static const Unaligned &at(const void *ptr) noexcept { + return *reinterpret_cast<const Unaligned*>(ptr); + } + T read() const noexcept { + T value; + static_assert(sizeof(_data) == sizeof(value)); + memcpy(&value, _data, sizeof(value)); + return value; + } + void write(const T &value) noexcept { + static_assert(sizeof(_data) == sizeof(value)); + memcpy(_data, &value, sizeof(value)); + } + operator T () const noexcept { return read(); } + Unaligned &operator=(const T &value) noexcept { + write(value); + return *this; + } +}; + /** * @brief Keep ownership of memory allocated via malloc() * @@ -97,9 +161,7 @@ public: if (_p == nullptr) { _sz = 0; } - if (_sz != 0) { - memcpy(_p, rhs.get(), _sz); - } + memcpy_safe(_p, rhs.get(), _sz); } /** |