aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHåvard Pettersen <havardpe@yahooinc.com>2022-06-09 10:29:01 +0000
committerHåvard Pettersen <havardpe@yahooinc.com>2022-06-10 14:12:30 +0000
commitce82a9d52c5f1676c2e4795f631269e7992b1877 (patch)
treed02f0a7cf5aab103d4d6e703617168fa0851e793
parent0768aded2eeca29027178260875fab85610c2c30 (diff)
common memory tools
-rw-r--r--vespalib/src/tests/memory/memory_test.cpp234
-rw-r--r--vespalib/src/vespa/vespalib/util/memory.h68
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);
}
/**